1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "private/qlayoutengine_p.h"
43 #include "qabstractitemdelegate.h"
44 #include "qapplication.h"
50 #include "qstyleoption.h"
51 #include "qstylepainter.h"
52 #include "qtabwidget.h"
54 #include "qwhatsthis.h"
55 #include "private/qtextengine_p.h"
56 #ifndef QT_NO_ACCESSIBILITY
57 #include "qaccessible.h"
61 #include "private/qtabbar_p.h"
66 #include <private/qt_mac_p.h>
67 #include <private/qt_cocoa_helpers_mac_p.h>
70 #ifndef QT_NO_STYLE_S60
71 #include "qs60style.h"
77 inline static bool verticalTabs(QTabBar::Shape shape)
79 return shape == QTabBar::RoundedWest
80 || shape == QTabBar::RoundedEast
81 || shape == QTabBar::TriangularWest
82 || shape == QTabBar::TriangularEast;
85 void QTabBarPrivate::updateMacBorderMetrics()
87 #if (defined Q_WS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
88 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
90 ::HIContentBorderMetrics metrics;
92 // TODO: get metrics to preserve the bottom value
93 // TODO: test tab bar position
95 OSWindowRef window = qt_mac_window_for(q);
97 // push base line separator down to the client are so we can paint over it (Carbon)
98 metrics.top = (documentMode && q->isVisible()) ? 1 : 0;
102 qt_mac_updateContentBorderMetricts(window, metrics);
104 // In Cocoa we need to keep track of the drawRect method.
105 // If documentMode is enabled we need to change it, unless
106 // a toolbar is present.
107 // Notice that all the information is kept in the window,
108 // that's why we get the private widget for it instead of
109 // the private widget for this widget.
110 QWidgetPrivate *privateWidget = qt_widget_private(q->window());
112 privateWidget->changeMethods = documentMode;
113 // Since in Cocoa there is no simple way to remove the baseline, so we just ask the
114 // top level to do the magic for us.
115 privateWidget->syncUnifiedMode();
116 #endif // QT_MAC_USE_COCOA
122 Initialize \a option with the values from the tab at \a tabIndex. This method
123 is useful for subclasses when they need a QStyleOptionTab, QStyleOptionTabV2,
124 or QStyleOptionTabV3 but don't want to fill in all the information themselves.
125 This function will check the version of the QStyleOptionTab and fill in the
126 additional values for a QStyleOptionTabV2 and QStyleOptionTabV3.
128 \sa QStyleOption::initFrom() QTabWidget::initStyleOption()
130 void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
133 int totalTabs = d->tabList.size();
135 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
138 const QTabBarPrivate::Tab &tab = d->tabList.at(tabIndex);
139 option->initFrom(this);
140 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
141 option->rect = tabRect(tabIndex);
142 bool isCurrent = tabIndex == d->currentIndex;
144 if (tabIndex == d->pressedIndex)
145 option->state |= QStyle::State_Sunken;
147 option->state |= QStyle::State_Selected;
148 if (isCurrent && hasFocus())
149 option->state |= QStyle::State_HasFocus;
151 option->state &= ~QStyle::State_Enabled;
152 if (isActiveWindow())
153 option->state |= QStyle::State_Active;
154 if (!d->dragInProgress && option->rect == d->hoverRect)
155 option->state |= QStyle::State_MouseOver;
156 option->shape = d->shape;
157 option->text = tab.text;
159 if (tab.textColor.isValid())
160 option->palette.setColor(foregroundRole(), tab.textColor);
162 option->icon = tab.icon;
163 if (QStyleOptionTabV2 *optionV2 = qstyleoption_cast<QStyleOptionTabV2 *>(option))
164 optionV2->iconSize = iconSize(); // Will get the default value then.
166 if (QStyleOptionTabV3 *optionV3 = qstyleoption_cast<QStyleOptionTabV3 *>(option)) {
167 optionV3->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
168 optionV3->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
169 optionV3->documentMode = d->documentMode;
172 if (tabIndex > 0 && tabIndex - 1 == d->currentIndex)
173 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
174 else if (tabIndex + 1 < totalTabs && tabIndex + 1 == d->currentIndex)
175 option->selectedPosition = QStyleOptionTab::NextIsSelected;
177 option->selectedPosition = QStyleOptionTab::NotAdjacent;
179 bool paintBeginning = (tabIndex == 0) || (d->dragInProgress && tabIndex == d->pressedIndex + 1);
180 bool paintEnd = (tabIndex == totalTabs - 1) || (d->dragInProgress && tabIndex == d->pressedIndex - 1);
181 if (paintBeginning) {
183 option->position = QStyleOptionTab::OnlyOneTab;
185 option->position = QStyleOptionTab::Beginning;
186 } else if (paintEnd) {
187 option->position = QStyleOptionTab::End;
189 option->position = QStyleOptionTab::Middle;
192 #ifndef QT_NO_TABWIDGET
193 if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(parentWidget())) {
194 if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
195 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
196 if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
197 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
201 QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
202 option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
203 Qt::TextShowMnemonic);
208 \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
210 \ingroup basicwidgets
213 QTabBar is straightforward to use; it draws the tabs using one of
214 the predefined \link QTabBar::Shape shapes\endlink, and emits a
215 signal when a tab is selected. It can be subclassed to tailor the
216 look and feel. Qt also provides a ready-made \l{QTabWidget}.
218 Each tab has a tabText(), an optional tabIcon(), an optional
219 tabToolTip(), optional tabWhatsThis() and optional tabData().
220 The tabs's attributes can be changed with setTabText(), setTabIcon(),
221 setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
222 enabled or disabled individually with setTabEnabled().
224 Each tab can display text in a distinct color. The current text color
225 for a tab can be found with the tabTextColor() function. Set the text
226 color for a particular tab with setTabTextColor().
228 Tabs are added using addTab(), or inserted at particular positions
229 using insertTab(). The total number of tabs is given by
230 count(). Tabs can be removed from the tab bar with
231 removeTab(). Combining removeTab() and insertTab() allows you to
232 move tabs to different positions.
234 The \l shape property defines the tabs' appearance. The choice of
235 shape is a matter of taste, although tab dialogs (for preferences
236 and similar) invariably use \l RoundedNorth.
237 Tab controls in windows other than dialogs almost
238 always use either \l RoundedSouth or \l TriangularSouth. Many
239 spreadsheets and other tab controls in which all the pages are
240 essentially similar use \l TriangularSouth, whereas \l
241 RoundedSouth is used mostly when the pages are different (e.g. a
242 multi-page tool palette). The default in QTabBar is \l
245 The most important part of QTabBar's API is the currentChanged()
246 signal. This is emitted whenever the current tab changes (even at
247 startup, when the current tab changes from 'none'). There is also
248 a slot, setCurrentIndex(), which can be used to select a tab
249 programmatically. The function currentIndex() returns the index of
250 the current tab, \l count holds the number of tabs.
252 QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
253 e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
254 key for switching to that tab.
256 The following virtual functions may need to be reimplemented in
257 order to tailor the look and feel or store extra data with each
261 \i tabSizeHint() calcuates the size of a tab.
262 \i tabInserted() notifies that a new tab was added.
263 \i tabRemoved() notifies that a tab was removed.
264 \i tabLayoutChange() notifies that the tabs have been re-laid out.
265 \i paintEvent() paints all tabs.
268 For subclasses, you might also need the tabRect() functions which
269 returns the visual geometry of a single tab.
272 \row \o \inlineimage plastique-tabbar.png Screenshot of a Plastique style tab bar
273 \o A tab bar shown in the Plastique widget style.
274 \row \o \inlineimage plastique-tabbar-truncated.png Screenshot of a truncated Plastique tab bar
275 \o A truncated tab bar shown in the Plastique widget style.
284 This enum type lists the built-in shapes supported by QTabBar. Treat these
285 as hints as some styles may not render some of the shapes. However,
286 position should be honored.
288 \value RoundedNorth The normal rounded look above the pages
290 \value RoundedSouth The normal rounded look below the pages
292 \value RoundedWest The normal rounded look on the left side of the pages
294 \value RoundedEast The normal rounded look on the right side the pages
296 \value TriangularNorth Triangular tabs above the pages.
298 \value TriangularSouth Triangular tabs similar to those used in
299 the Excel spreadsheet, for example
301 \value TriangularWest Triangular tabs on the left of the pages.
303 \value TriangularEast Triangular tabs on the right of the pages.
304 \omitvalue RoundedAbove
305 \omitvalue RoundedBelow
306 \omitvalue TriangularAbove
307 \omitvalue TriangularBelow
311 \fn void QTabBar::currentChanged(int index)
313 This signal is emitted when the tab bar's current tab changes. The
314 new current has the given \a index, or -1 if there isn't a new one
315 (for example, if there are no tab in the QTabBar)
319 \fn void QTabBar::tabCloseRequested(int index)
322 This signal is emitted when the close button on a tab is clicked.
323 The \a index is the index that should be removed.
325 \sa setTabsClosable()
329 \fn void QTabBar::tabMoved(int from, int to)
332 This signal is emitted when the tab has moved the tab
333 at index position \a from to index position \a to.
335 note: QTabWidget will automatically move the page when
336 this signal is emitted from its tab bar.
341 int QTabBarPrivate::extraWidth() const
344 return 2 * qMax(q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, q),
345 QApplication::globalStrut().width());
348 void QTabBarPrivate::init()
351 leftB = new QToolButton(q);
352 leftB->setAutoRepeat(true);
353 QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
355 rightB = new QToolButton(q);
356 rightB->setAutoRepeat(true);
357 QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
359 #ifdef QT_KEYPAD_NAVIGATION
360 if (QApplication::keypadNavigationEnabled()) {
361 leftB->setFocusPolicy(Qt::NoFocus);
362 rightB->setFocusPolicy(Qt::NoFocus);
363 q->setFocusPolicy(Qt::NoFocus);
366 q->setFocusPolicy(Qt::TabFocus);
367 q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
368 elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
369 useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
372 QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
374 return validIndex(index)?&tabList[index]:0;
377 const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
379 return validIndex(index)?&tabList[index]:0;
382 int QTabBarPrivate::indexAtPos(const QPoint &p) const
385 if (q->tabRect(currentIndex).contains(p))
387 for (int i = 0; i < tabList.count(); ++i)
388 if (tabList.at(i).enabled && q->tabRect(i).contains(p))
393 void QTabBarPrivate::layoutTabs()
398 QSize size = q->size();
402 bool vertTabs = verticalTabs(shape);
403 int tabChainIndex = 0;
405 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
406 QVector<QLayoutStruct> tabChain(tabList.count() + 2);
408 // We put an empty item at the front and back and set its expansive attribute
409 // depending on tabAlignment.
410 tabChain[tabChainIndex].init();
411 tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignLeft)
412 && (tabAlignment != Qt::AlignJustify);
413 tabChain[tabChainIndex].empty = true;
416 // We now go through our list of tabs and set the minimum size and the size hint
417 // This will allow us to elide text if necessary. Since we don't set
418 // a maximum size, tabs will EXPAND to fill up the empty space.
419 // Since tab widget is rather *ahem* strict about keeping the geometry of the
420 // tab bar to its absolute minimum, this won't bleed through, but will show up
421 // if you use tab bar on its own (a.k.a. not a bug, but a feature).
422 // Update: if expanding is false, we DO set a maximum size to prevent the tabs
423 // being wider than necessary.
428 for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
429 QSize sz = q->tabSizeHint(i);
430 tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
432 maxHeight = qMax(maxHeight, sz.height());
433 sz = minimumTabSizeHint(i);
434 tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
436 tabChain[tabChainIndex].init();
437 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
438 tabChain[tabChainIndex].minimumSize = sz.width();
439 tabChain[tabChainIndex].empty = false;
440 tabChain[tabChainIndex].expansive = true;
443 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
447 available = size.width();
448 maxExtent = maxHeight;
453 for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
454 QSize sz = q->tabSizeHint(i);
455 tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
457 maxWidth = qMax(maxWidth, sz.width());
458 sz = minimumTabSizeHint(i);
459 tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
461 tabChain[tabChainIndex].init();
462 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
463 tabChain[tabChainIndex].minimumSize = sz.height();
464 tabChain[tabChainIndex].empty = false;
465 tabChain[tabChainIndex].expansive = true;
468 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
472 available = size.height();
473 maxExtent = maxWidth;
476 Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
477 // Mirror our front item.
478 tabChain[tabChainIndex].init();
479 tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
480 && (tabAlignment != Qt::AlignJustify);
481 tabChain[tabChainIndex].empty = true;
483 // Do the calculation
484 qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
487 for (i = 0; i < tabList.count(); ++i) {
488 const QLayoutStruct &lstruct = tabChain.at(i + 1);
490 tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
492 tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
495 if (useScrollButtons && tabList.count() && last > available) {
496 int extra = extraWidth();
497 #ifndef QT_NO_STYLE_S60
498 QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style());
501 Qt::LayoutDirection ld = q->layoutDirection();
502 QRect arrows = QStyle::visualRect(ld, q->rect(),
503 QRect(available - extra, 0, extra, size.height()));
504 int buttonOverlap = q->style()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, q);
506 if (ld == Qt::LeftToRight) {
507 // In S60style, tab scroll buttons are layoutted separately, on the sides of the tabbar.
508 #ifndef QT_NO_STYLE_S60
510 rightB->setGeometry(arrows.left() + extra / 2, arrows.top(), extra / 2, arrows.height());
511 leftB->setGeometry(0, arrows.top(), extra / 2, arrows.height());
514 leftB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
515 rightB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
516 extra/2, arrows.height());
517 #ifndef QT_NO_STYLE_S60
520 leftB->setArrowType(Qt::LeftArrow);
521 rightB->setArrowType(Qt::RightArrow);
523 #ifndef QT_NO_STYLE_S60
525 rightB->setGeometry(arrows.left() + extra / 2, arrows.top(), extra / 2, arrows.height());
526 leftB->setGeometry(0, arrows.top(), extra / 2, arrows.height());
529 rightB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
530 leftB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
531 extra/2, arrows.height());
532 #ifndef QT_NO_STYLE_S60
535 rightB->setArrowType(Qt::LeftArrow);
536 leftB->setArrowType(Qt::RightArrow);
539 #ifndef QT_NO_STYLE_S60
541 QRect arrows = QRect(0, 0, size.width(), available );
542 leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra / 2);
543 leftB->setArrowType(Qt::UpArrow);
544 rightB->setGeometry(arrows.left(), arrows.bottom() - extra / 2 + 1,
545 arrows.width(), extra / 2);
546 rightB->setArrowType(Qt::DownArrow);
549 QRect arrows = QRect(0, available - extra, size.width(), extra );
550 leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra/2);
551 leftB->setArrowType(Qt::UpArrow);
552 rightB->setGeometry(arrows.left(), arrows.bottom() - extra/2 + 1,
553 arrows.width(), extra/2);
554 rightB->setArrowType(Qt::DownArrow);
555 #ifndef QT_NO_STYLE_S60
559 leftB->setEnabled(scrollOffset > 0);
560 rightB->setEnabled(last - scrollOffset >= available - extra);
569 q->tabLayoutChange();
572 void QTabBarPrivate::makeVisible(int index)
575 if (!validIndex(index) || leftB->isHidden())
578 const QRect tabRect = tabList.at(index).rect;
579 const int oldScrollOffset = scrollOffset;
580 const bool horiz = !verticalTabs(shape);
581 const int available = (horiz ? q->width() : q->height()) - extraWidth();
582 const int start = horiz ? tabRect.left() : tabRect.top();
583 const int end = horiz ? tabRect.right() : tabRect.bottom();
584 if (start < scrollOffset) // too far left
585 scrollOffset = start - (index ? 8 : 0);
586 else if (end > scrollOffset + available) // too far right
587 scrollOffset = end - available + 1;
589 leftB->setEnabled(scrollOffset > 0);
590 const int last = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom();
591 rightB->setEnabled(last - scrollOffset >= available);
592 if (oldScrollOffset != scrollOffset) {
598 void QTabBarPrivate::layoutTab(int index)
601 Q_ASSERT(index >= 0);
603 Tab &tab = tabList[index];
604 bool vertical = verticalTabs(shape);
605 if (!(tab.leftWidget || tab.rightWidget))
608 QStyleOptionTabV3 opt;
609 q->initStyleOption(&opt, index);
610 if (tab.leftWidget) {
611 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
612 QPoint p = rect.topLeft();
613 if ((index == pressedIndex) || paintWithOffsets) {
615 p.setY(p.y() + tabList[index].dragOffset);
617 p.setX(p.x() + tabList[index].dragOffset);
619 tab.leftWidget->move(p);
621 if (tab.rightWidget) {
622 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
623 QPoint p = rect.topLeft();
624 if ((index == pressedIndex) || paintWithOffsets) {
626 p.setY(p.y() + tab.dragOffset);
628 p.setX(p.x() + tab.dragOffset);
630 tab.rightWidget->move(p);
634 void QTabBarPrivate::layoutWidgets(int start)
637 for (int i = start; i < q->count(); ++i) {
642 void QTabBarPrivate::_q_closeTab()
645 QObject *object = q->sender();
647 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, q);
648 for (int i = 0; i < tabList.count(); ++i) {
649 if (closeSide == QTabBar::LeftSide) {
650 if (tabList.at(i).leftWidget == object) {
655 if (tabList.at(i).rightWidget == object) {
661 if (tabToClose != -1)
662 emit q->tabCloseRequested(tabToClose);
665 void QTabBarPrivate::_q_scrollTabs()
668 const QObject *sender = q->sender();
670 if (!verticalTabs(shape)) {
671 if (sender == leftB) {
672 for (i = tabList.count() - 1; i >= 0; --i) {
673 if (tabList.at(i).rect.left() - scrollOffset < 0) {
678 } else if (sender == rightB) {
679 int availableWidth = q->width() - extraWidth();
680 for (i = 0; i < tabList.count(); ++i) {
681 if (tabList.at(i).rect.right() - scrollOffset > availableWidth) {
688 if (sender == leftB) {
689 for (i = tabList.count() - 1; i >= 0; --i) {
690 if (tabList.at(i).rect.top() - scrollOffset < 0) {
695 } else if (sender == rightB) {
696 int available = q->height() - extraWidth();
697 for (i = 0; i < tabList.count(); ++i) {
698 if (tabList.at(i).rect.bottom() - scrollOffset > available) {
707 void QTabBarPrivate::refresh()
711 // be safe in case a subclass is also handling move with the tabs
712 if (pressedIndex != -1
714 && QApplication::mouseButtons() == Qt::NoButton) {
715 moveTabFinished(pressedIndex);
716 if (!validIndex(pressedIndex))
720 if (!q->isVisible()) {
724 makeVisible(currentIndex);
731 Creates a new tab bar with the given \a parent.
733 QTabBar::QTabBar(QWidget* parent)
734 :QWidget(*new QTabBarPrivate, parent, 0)
742 Destroys the tab bar.
749 \property QTabBar::shape
750 \brief the shape of the tabs in the tab bar
752 Possible values for this property are described by the Shape enum.
756 QTabBar::Shape QTabBar::shape() const
762 void QTabBar::setShape(Shape shape)
765 if (d->shape == shape)
772 \property QTabBar::drawBase
773 \brief defines whether or not tab bar should draw its base.
775 If true then QTabBar draws a base in relation to the styles overlab.
776 Otherwise only the tabs are drawn.
778 \sa QStyle::pixelMetric() QStyle::PM_TabBarBaseOverlap QStyleOptionTabBarBaseV2
781 void QTabBar::setDrawBase(bool drawBase)
784 if (d->drawBase == drawBase)
786 d->drawBase = drawBase;
790 bool QTabBar::drawBase() const
797 Adds a new tab with text \a text. Returns the new
800 int QTabBar::addTab(const QString &text)
802 return insertTab(-1, text);
808 Adds a new tab with icon \a icon and text \a
809 text. Returns the new tab's index.
811 int QTabBar::addTab(const QIcon& icon, const QString &text)
813 return insertTab(-1, icon, text);
817 Inserts a new tab with text \a text at position \a index. If \a
818 index is out of range, the new tab is appened. Returns the new
821 int QTabBar::insertTab(int index, const QString &text)
823 return insertTab(index, QIcon(), text);
828 Inserts a new tab with icon \a icon and text \a text at position
829 \a index. If \a index is out of range, the new tab is
830 appended. Returns the new tab's index.
832 If the QTabBar was empty before this function is called, the inserted tab
833 becomes the current tab.
835 Inserting a new tab at an index less than or equal to the current index
836 will increment the current index, but keep the current tab.
838 int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
841 if (!d->validIndex(index)) {
842 index = d->tabList.count();
843 d->tabList.append(QTabBarPrivate::Tab(icon, text));
845 d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
847 #ifndef QT_NO_SHORTCUT
848 d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
851 if (d->tabList.count() == 1)
852 setCurrentIndex(index);
853 else if (index <= d->currentIndex)
856 if (d->closeButtonOnTabs) {
857 QStyleOptionTabV3 opt;
858 initStyleOption(&opt, index);
859 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
860 QAbstractButton *closeButton = new CloseButton(this);
861 connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
862 setTabButton(index, closeSide, closeButton);
865 for (int i = 0; i < d->tabList.count(); ++i) {
866 if (d->tabList[i].lastTab >= index)
867 ++d->tabList[i].lastTab;
876 Removes the tab at position \a index.
878 \sa SelectionBehavior
880 void QTabBar::removeTab(int index)
883 if (d->validIndex(index)) {
884 #ifndef QT_NO_SHORTCUT
885 releaseShortcut(d->tabList.at(index).shortcutId);
887 if (d->tabList[index].leftWidget) {
888 d->tabList[index].leftWidget->hide();
889 d->tabList[index].leftWidget->deleteLater();
890 d->tabList[index].leftWidget = 0;
892 if (d->tabList[index].rightWidget) {
893 d->tabList[index].rightWidget->hide();
894 d->tabList[index].rightWidget->deleteLater();
895 d->tabList[index].rightWidget = 0;
898 int newIndex = d->tabList[index].lastTab;
899 d->tabList.removeAt(index);
900 for (int i = 0; i < d->tabList.count(); ++i) {
901 if (d->tabList[i].lastTab == index)
902 d->tabList[i].lastTab = -1;
903 if (d->tabList[i].lastTab > index)
904 --d->tabList[i].lastTab;
906 if (index == d->currentIndex) {
907 // The current tab is going away, in order to make sure
908 // we emit that "current has changed", we need to reset this
910 d->currentIndex = -1;
911 if (d->tabList.size() > 0) {
912 switch(d->selectionBehaviorOnRemove) {
913 case SelectPreviousTab:
914 if (newIndex > index)
916 if (d->validIndex(newIndex))
921 if (newIndex >= d->tabList.size())
922 newIndex = d->tabList.size() - 1;
925 newIndex = index - 1;
933 if (d->validIndex(newIndex)) {
934 // don't loose newIndex's old through setCurrentIndex
935 int bump = d->tabList[newIndex].lastTab;
936 setCurrentIndex(newIndex);
937 d->tabList[newIndex].lastTab = bump;
940 emit currentChanged(-1);
942 } else if (index < d->currentIndex) {
943 setCurrentIndex(d->currentIndex - 1);
952 Returns true if the tab at position \a index is enabled; otherwise
955 bool QTabBar::isTabEnabled(int index) const
958 if (const QTabBarPrivate::Tab *tab = d->at(index))
964 If \a enabled is true then the tab at position \a index is
965 enabled; otherwise the item at position \a index is disabled.
967 void QTabBar::setTabEnabled(int index, bool enabled)
970 if (QTabBarPrivate::Tab *tab = d->at(index)) {
971 tab->enabled = enabled;
972 #ifndef QT_NO_SHORTCUT
973 setShortcutEnabled(tab->shortcutId, enabled);
976 if (!enabled && index == d->currentIndex)
977 setCurrentIndex(d->validIndex(index+1)?index+1:0);
978 else if (enabled && !d->validIndex(d->currentIndex))
979 setCurrentIndex(index);
985 Returns the text of the tab at position \a index, or an empty
986 string if \a index is out of range.
988 QString QTabBar::tabText(int index) const
991 if (const QTabBarPrivate::Tab *tab = d->at(index))
997 Sets the text of the tab at position \a index to \a text.
999 void QTabBar::setTabText(int index, const QString &text)
1002 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1004 #ifndef QT_NO_SHORTCUT
1005 releaseShortcut(tab->shortcutId);
1006 tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1007 setShortcutEnabled(tab->shortcutId, tab->enabled);
1014 Returns the text color of the tab with the given \a index, or a invalid
1015 color if \a index is out of range.
1017 \sa setTabTextColor()
1019 QColor QTabBar::tabTextColor(int index) const
1022 if (const QTabBarPrivate::Tab *tab = d->at(index))
1023 return tab->textColor;
1028 Sets the color of the text in the tab with the given \a index to the specified \a color.
1030 If an invalid color is specified, the tab will use the QTabBar foreground role instead.
1034 void QTabBar::setTabTextColor(int index, const QColor &color)
1037 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1038 tab->textColor = color;
1039 update(tabRect(index));
1044 Returns the icon of the tab at position \a index, or a null icon
1045 if \a index is out of range.
1047 QIcon QTabBar::tabIcon(int index) const
1050 if (const QTabBarPrivate::Tab *tab = d->at(index))
1056 Sets the icon of the tab at position \a index to \a icon.
1058 void QTabBar::setTabIcon(int index, const QIcon & icon)
1061 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1062 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1064 if (simpleIconChange)
1065 update(tabRect(index));
1071 #ifndef QT_NO_TOOLTIP
1073 Sets the tool tip of the tab at position \a index to \a tip.
1075 void QTabBar::setTabToolTip(int index, const QString & tip)
1078 if (QTabBarPrivate::Tab *tab = d->at(index))
1083 Returns the tool tip of the tab at position \a index, or an empty
1084 string if \a index is out of range.
1086 QString QTabBar::tabToolTip(int index) const
1089 if (const QTabBarPrivate::Tab *tab = d->at(index))
1090 return tab->toolTip;
1093 #endif // QT_NO_TOOLTIP
1095 #ifndef QT_NO_WHATSTHIS
1099 Sets the What's This help text of the tab at position \a index
1102 void QTabBar::setTabWhatsThis(int index, const QString &text)
1105 if (QTabBarPrivate::Tab *tab = d->at(index))
1106 tab->whatsThis = text;
1112 Returns the What's This help text of the tab at position \a index,
1113 or an empty string if \a index is out of range.
1115 QString QTabBar::tabWhatsThis(int index) const
1118 if (const QTabBarPrivate::Tab *tab = d->at(index))
1119 return tab->whatsThis;
1123 #endif // QT_NO_WHATSTHIS
1126 Sets the data of the tab at position \a index to \a data.
1128 void QTabBar::setTabData(int index, const QVariant & data)
1131 if (QTabBarPrivate::Tab *tab = d->at(index))
1136 Returns the data of the tab at position \a index, or a null
1137 variant if \a index is out of range.
1139 QVariant QTabBar::tabData(int index) const
1142 if (const QTabBarPrivate::Tab *tab = d->at(index))
1148 Returns the visual rectangle of the tab at position \a
1149 index, or a null rectangle if \a index is out of range.
1151 QRect QTabBar::tabRect(int index) const
1154 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1156 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1157 QRect r = tab->rect;
1158 if (verticalTabs(d->shape))
1159 r.translate(0, -d->scrollOffset);
1161 r.translate(-d->scrollOffset, 0);
1162 if (!verticalTabs(d->shape))
1163 r = QStyle::visualRect(layoutDirection(), rect(), r);
1171 Returns the index of the tab that covers \a position or -1 if no
1172 tab covers \a position;
1175 int QTabBar::tabAt(const QPoint &position) const
1178 if (d->validIndex(d->currentIndex)
1179 && tabRect(d->currentIndex).contains(position)) {
1180 return d->currentIndex;
1182 const int max = d->tabList.size();
1183 for (int i = 0; i < max; ++i) {
1184 if (tabRect(i).contains(position)) {
1192 \property QTabBar::currentIndex
1193 \brief the index of the tab bar's visible tab
1195 The current index is -1 if there is no current tab.
1198 int QTabBar::currentIndex() const
1201 if (d->validIndex(d->currentIndex))
1202 return d->currentIndex;
1207 void QTabBar::setCurrentIndex(int index)
1210 if (d->dragInProgress && d->pressedIndex != -1)
1213 int oldIndex = d->currentIndex;
1214 if (d->validIndex(index) && d->currentIndex != index) {
1215 d->currentIndex = index;
1217 d->makeVisible(index);
1218 d->tabList[index].lastTab = oldIndex;
1219 if (oldIndex >= 0 && oldIndex < count())
1220 d->layoutTab(oldIndex);
1221 d->layoutTab(index);
1222 #ifndef QT_NO_ACCESSIBILITY
1223 if (QAccessible::isActive()) {
1224 QAccessible::updateAccessibility(this, index + 1, QAccessible::Focus);
1225 QAccessible::updateAccessibility(this, index + 1, QAccessible::Selection);
1229 emit selected(index);
1231 emit currentChanged(index);
1236 \property QTabBar::iconSize
1237 \brief The size for icons in the tab bar
1240 The default value is style-dependent. \c iconSize is a maximum
1241 size; icons that are smaller are not scaled up.
1243 \sa QTabWidget::iconSize
1245 QSize QTabBar::iconSize() const
1248 if (d->iconSize.isValid())
1250 int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
1251 return QSize(iconExtent, iconExtent);
1255 void QTabBar::setIconSize(const QSize &size)
1259 d->layoutDirty = true;
1265 \property QTabBar::count
1266 \brief the number of tabs in the tab bar
1269 int QTabBar::count() const
1272 return d->tabList.count();
1278 QSize QTabBar::sizeHint() const
1282 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1284 for (int i = 0; i < d->tabList.count(); ++i)
1285 r = r.united(d->tabList.at(i).maxRect);
1286 QSize sz = QApplication::globalStrut();
1287 return r.size().expandedTo(sz);
1292 QSize QTabBar::minimumSizeHint() const
1296 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1297 if (!d->useScrollButtons) {
1299 for (int i = 0; i < d->tabList.count(); ++i)
1300 r = r.united(d->tabList.at(i).minRect);
1301 return r.size().expandedTo(QApplication::globalStrut());
1303 if (verticalTabs(d->shape))
1304 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1306 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1309 // Compute the most-elided possible text, for minimumSizeHint
1310 static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
1312 if (text.length() <= 3)
1315 static const QLatin1String Ellipses("...");
1318 case Qt::ElideRight:
1319 ret = text.left(2) + Ellipses;
1321 case Qt::ElideMiddle:
1322 ret = text.left(1) + Ellipses + text.right(1);
1325 ret = Ellipses + text.right(2);
1334 QSize QTabBarPrivate::minimumTabSizeHint(int index)
1337 // ### Qt 5: make this a protected virtual function in QTabBar
1338 Tab &tab = tabList[index];
1339 QString oldText = tab.text;
1340 tab.text = computeElidedText(elideMode, oldText);
1341 QSize size = q->tabSizeHint(index);
1347 Returns the size hint for the tab at position \a index.
1349 QSize QTabBar::tabSizeHint(int index) const
1351 //Note: this must match with the computations in QCommonStylePrivate::tabLayout
1353 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1354 QStyleOptionTabV3 opt;
1355 initStyleOption(&opt, index);
1356 opt.text = d->tabList.at(index).text;
1357 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1358 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
1359 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
1360 const QFontMetrics fm = fontMetrics();
1362 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1363 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1365 int widgetWidth = 0;
1366 int widgetHeight = 0;
1368 if (!opt.leftButtonSize.isEmpty()) {
1370 widgetWidth += opt.leftButtonSize.width();
1371 widgetHeight += opt.leftButtonSize.height();
1373 if (!opt.rightButtonSize.isEmpty()) {
1375 widgetWidth += opt.rightButtonSize.width();
1376 widgetHeight += opt.rightButtonSize.height();
1378 if (!opt.icon.isNull())
1382 if (verticalTabs(d->shape)) {
1383 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1384 fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe + widgetHeight + padding);
1386 csz = QSize(fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe
1387 + widgetWidth + padding,
1388 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1391 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
1398 This virtual handler is called after a new tab was added or
1399 inserted at position \a index.
1403 void QTabBar::tabInserted(int index)
1409 This virtual handler is called after a tab was removed from
1414 void QTabBar::tabRemoved(int index)
1420 This virtual handler is called whenever the tab layout changes.
1424 void QTabBar::tabLayoutChange()
1431 void QTabBar::showEvent(QShowEvent *)
1436 if (!d->validIndex(d->currentIndex))
1438 d->updateMacBorderMetrics();
1443 void QTabBar::hideEvent(QHideEvent *)
1446 d->updateMacBorderMetrics();
1451 bool QTabBar::event(QEvent *event)
1454 if (event->type() == QEvent::HoverMove
1455 || event->type() == QEvent::HoverEnter) {
1456 QHoverEvent *he = static_cast<QHoverEvent *>(event);
1457 if (!d->hoverRect.contains(he->pos())) {
1458 QRect oldHoverRect = d->hoverRect;
1459 for (int i = 0; i < d->tabList.count(); ++i) {
1460 QRect area = tabRect(i);
1461 if (area.contains(he->pos())) {
1462 d->hoverRect = area;
1466 if (he->oldPos() != QPoint(-1, -1))
1467 update(oldHoverRect);
1468 update(d->hoverRect);
1471 } else if (event->type() == QEvent::HoverLeave ) {
1472 QRect oldHoverRect = d->hoverRect;
1473 d->hoverRect = QRect();
1474 update(oldHoverRect);
1476 #ifndef QT_NO_TOOLTIP
1477 } else if (event->type() == QEvent::ToolTip) {
1478 if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
1479 if (!tab->toolTip.isEmpty()) {
1480 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
1484 #endif // QT_NO_TOOLTIP
1485 #ifndef QT_NO_WHATSTHIS
1486 } else if (event->type() == QEvent::QueryWhatsThis) {
1487 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
1488 if (!tab || tab->whatsThis.isEmpty())
1491 } else if (event->type() == QEvent::WhatsThis) {
1492 if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
1493 if (!tab->whatsThis.isEmpty()) {
1494 QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
1495 tab->whatsThis, this);
1499 #endif // QT_NO_WHATSTHIS
1500 #ifndef QT_NO_SHORTCUT
1501 } else if (event->type() == QEvent::Shortcut) {
1502 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1503 for (int i = 0; i < d->tabList.count(); ++i) {
1504 const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
1505 if (tab->shortcutId == se->shortcutId()) {
1512 return QWidget::event(event);
1517 void QTabBar::resizeEvent(QResizeEvent *)
1524 d->makeVisible(d->currentIndex);
1529 void QTabBar::paintEvent(QPaintEvent *)
1533 QStyleOptionTabBarBaseV2 optTabBase;
1534 QTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());
1536 QStylePainter p(this);
1539 bool rtl = optTabBase.direction == Qt::RightToLeft;
1540 bool vertical = verticalTabs(d->shape);
1541 QStyleOptionTab cutTab;
1542 selected = d->currentIndex;
1543 if (d->dragInProgress)
1544 selected = d->pressedIndex;
1546 for (int i = 0; i < d->tabList.count(); ++i)
1547 optTabBase.tabBarRect |= tabRect(i);
1549 optTabBase.selectedTabRect = tabRect(selected);
1552 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1554 for (int i = 0; i < d->tabList.count(); ++i) {
1555 QStyleOptionTabV3 tab;
1556 initStyleOption(&tab, i);
1557 if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
1559 tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
1561 tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
1564 if (!(tab.state & QStyle::State_Enabled)) {
1565 tab.palette.setCurrentColorGroup(QPalette::Disabled);
1567 // If this tab is partially obscured, make a note of it so that we can pass the information
1568 // along when we draw the tear.
1569 if (((!vertical && (!rtl && tab.rect.left() < 0)) || (rtl && tab.rect.right() > width()))
1570 || (vertical && tab.rect.top() < 0)) {
1574 // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
1575 if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
1576 || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
1579 optTabBase.tabBarRect |= tab.rect;
1583 p.drawControl(QStyle::CE_TabBarTab, tab);
1586 // Draw the selected tab last to get it "on top"
1587 if (selected >= 0) {
1588 QStyleOptionTabV3 tab;
1589 initStyleOption(&tab, selected);
1590 if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
1592 tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
1594 tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
1596 if (!d->dragInProgress)
1597 p.drawControl(QStyle::CE_TabBarTab, tab);
1599 int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, this);
1600 d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
1604 // Only draw the tear indicator if necessary. Most of the time we don't need too.
1605 if (d->leftB->isVisible() && cut >= 0) {
1606 cutTab.rect = rect();
1607 cutTab.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicator, &cutTab, this);
1608 p.drawPrimitive(QStyle::PE_IndicatorTabTear, cutTab);
1613 Given that index at position from moved to position to where return where index goes.
1615 int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
1620 int start = qMin(from, to);
1621 int end = qMax(from, to);
1622 if (index >= start && index <= end)
1623 index += (from < to) ? -1 : 1;
1628 Moves the item at index position \a from to index position \a to.
1631 \sa tabMoved(), tabLayoutChange()
1633 void QTabBar::moveTab(int from, int to)
1637 || !d->validIndex(from)
1638 || !d->validIndex(to))
1641 bool vertical = verticalTabs(d->shape);
1642 int oldPressedPosition = 0;
1643 if (d->pressedIndex != -1) {
1644 // Record the position of the pressed tab before reordering the tabs.
1645 oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
1646 : d->tabList[d->pressedIndex].rect.x();
1649 // Update the locations of the tabs first
1650 int start = qMin(from, to);
1651 int end = qMax(from, to);
1652 int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
1655 bool rtl = isRightToLeft();
1656 for (int i = start; i <= end; ++i) {
1660 d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
1662 d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
1664 if (rtl && !vertical)
1666 if (d->tabList[i].dragOffset != 0)
1667 d->tabList[i].dragOffset += (direction * width);
1672 d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
1674 d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
1677 d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
1679 d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
1682 // Move the actual data structures
1683 d->tabList.move(from, to);
1685 // update lastTab locations
1686 for (int i = 0; i < d->tabList.count(); ++i)
1687 d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
1689 // update external variables
1690 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
1692 // If we are in the middle of a drag update the dragStartPosition
1693 if (d->pressedIndex != -1) {
1694 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
1695 int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
1696 int diff = oldPressedPosition - newPressedPosition;
1697 if (isRightToLeft() && !vertical)
1700 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
1702 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
1705 d->layoutWidgets(start);
1707 emit tabMoved(from, to);
1708 emit tabLayoutChange();
1711 void QTabBarPrivate::slide(int from, int to)
1715 || !validIndex(from)
1718 bool vertical = verticalTabs(shape);
1719 int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
1720 q->setUpdatesEnabled(false);
1721 q->moveTab(from, to);
1722 q->setUpdatesEnabled(true);
1723 int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
1724 int length = postLocation - preLocation;
1725 tabList[to].dragOffset -= length;
1726 tabList[to].startAnimation(this, ANIMATION_DURATION);
1729 void QTabBarPrivate::moveTab(int index, int offset)
1731 if (!validIndex(index))
1733 tabList[index].dragOffset = offset;
1734 layoutTab(index); // Make buttons follow tab
1740 void QTabBar::mousePressEvent(QMouseEvent *event)
1743 if (event->button() != Qt::LeftButton) {
1748 if (d->pressedIndex != -1 && d->movable)
1749 d->moveTabFinished(d->pressedIndex);
1751 d->pressedIndex = d->indexAtPos(event->pos());
1753 d->previousPressedIndex = d->pressedIndex;
1755 if (d->validIndex(d->pressedIndex)) {
1756 QStyleOptionTabBarBaseV2 optTabBase;
1757 optTabBase.init(this);
1758 optTabBase.documentMode = d->documentMode;
1759 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
1760 setCurrentIndex(d->pressedIndex);
1762 repaint(tabRect(d->pressedIndex));
1764 d->dragStartPosition = event->pos();
1771 void QTabBar::mouseMoveEvent(QMouseEvent *event)
1776 if (d->pressedIndex != -1
1777 && event->buttons() == Qt::NoButton)
1778 d->moveTabFinished(d->pressedIndex);
1781 if (!d->dragInProgress && d->pressedIndex != -1) {
1782 if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
1783 d->dragInProgress = true;
1784 d->setupMovableTab();
1788 int offset = (event->pos() - d->dragStartPosition).manhattanLength();
1789 if (event->buttons() == Qt::LeftButton
1790 && offset > QApplication::startDragDistance()
1791 && d->validIndex(d->pressedIndex)) {
1792 bool vertical = verticalTabs(d->shape);
1795 dragDistance = (event->pos().y() - d->dragStartPosition.y());
1797 dragDistance = (event->pos().x() - d->dragStartPosition.x());
1799 d->tabList[d->pressedIndex].dragOffset = dragDistance;
1801 QRect startingRect = tabRect(d->pressedIndex);
1803 startingRect.moveTop(startingRect.y() + dragDistance);
1805 startingRect.moveLeft(startingRect.x() + dragDistance);
1808 if (dragDistance < 0)
1809 overIndex = tabAt(startingRect.topLeft());
1811 overIndex = tabAt(startingRect.topRight());
1813 if (overIndex != d->pressedIndex && overIndex != -1) {
1815 if (isRightToLeft() && !vertical)
1817 if (dragDistance < 0) {
1821 for (int i = d->pressedIndex;
1822 offset > 0 ? i < overIndex : i > overIndex;
1824 QRect overIndexRect = tabRect(overIndex);
1825 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
1826 if (dragDistance > needsToBeOver)
1827 d->slide(i + offset, d->pressedIndex);
1830 // Buttons needs to follow the dragged tab
1831 d->layoutTab(d->pressedIndex);
1836 } else if (!d->documentMode && event->buttons() == Qt::LeftButton && d->previousPressedIndex != -1) {
1837 int newPressedIndex = d->indexAtPos(event->pos());
1838 if (d->pressedIndex == -1 && d->previousPressedIndex == newPressedIndex) {
1839 d->pressedIndex = d->previousPressedIndex;
1840 update(tabRect(d->pressedIndex));
1841 } else if(d->pressedIndex != newPressedIndex) {
1842 d->pressedIndex = -1;
1843 update(tabRect(d->previousPressedIndex));
1848 if (event->buttons() != Qt::LeftButton) {
1852 QStyleOptionTabBarBaseV2 optTabBase;
1853 optTabBase.init(this);
1854 optTabBase.documentMode = d->documentMode;
1857 void QTabBarPrivate::setupMovableTab()
1861 movingTab = new QWidget(q);
1863 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0 ,q);
1864 QRect grabRect = q->tabRect(pressedIndex);
1865 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
1867 QPixmap grabImage(grabRect.size());
1868 grabImage.fill(Qt::transparent);
1869 QStylePainter p(&grabImage, q);
1872 QStyleOptionTabV3 tab;
1873 q->initStyleOption(&tab, pressedIndex);
1874 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
1875 p.drawControl(QStyle::CE_TabBarTab, tab);
1879 pal.setBrush(QPalette::All, QPalette::Window, grabImage);
1880 movingTab->setPalette(pal);
1881 movingTab->setGeometry(grabRect);
1882 movingTab->setAutoFillBackground(true);
1885 // Re-arrange widget order to avoid overlaps
1886 if (tabList[pressedIndex].leftWidget)
1887 tabList[pressedIndex].leftWidget->raise();
1888 if (tabList[pressedIndex].rightWidget)
1889 tabList[pressedIndex].rightWidget->raise();
1894 movingTab->setVisible(true);
1897 void QTabBarPrivate::moveTabFinished(int index)
1900 bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
1901 bool allAnimationsFinished = true;
1902 #ifndef QT_NO_ANIMATION
1903 for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
1904 const Tab &t = tabList.at(i);
1905 if (t.animation && t.animation->state() == QAbstractAnimation::Running)
1906 allAnimationsFinished = false;
1908 #endif //QT_NO_ANIMATION
1909 if (allAnimationsFinished && cleanup) {
1911 movingTab->setVisible(false); // We might not get a mouse release
1912 for (int i = 0; i < tabList.count(); ++i) {
1913 tabList[i].dragOffset = 0;
1915 if (pressedIndex != -1 && movable) {
1917 dragInProgress = false;
1918 dragStartPosition = QPoint();
1922 if (!validIndex(index))
1924 tabList[index].dragOffset = 0;
1931 void QTabBar::mouseReleaseEvent(QMouseEvent *event)
1934 if (event->button() != Qt::LeftButton) {
1939 d->previousPressedIndex = -1;
1941 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
1942 int length = d->tabList[d->pressedIndex].dragOffset;
1943 int width = verticalTabs(d->shape)
1944 ? tabRect(d->pressedIndex).height()
1945 : tabRect(d->pressedIndex).width();
1946 int duration = qMin(ANIMATION_DURATION,
1947 (qAbs(length) * ANIMATION_DURATION) / width);
1948 d->tabList[d->pressedIndex].startAnimation(d, duration);
1949 d->dragInProgress = false;
1950 d->movingTab->setVisible(false);
1951 d->dragStartPosition = QPoint();
1954 int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
1955 d->pressedIndex = -1;
1956 QStyleOptionTabBarBaseV2 optTabBase;
1957 optTabBase.initFrom(this);
1958 optTabBase.documentMode = d->documentMode;
1959 if (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease)
1965 void QTabBar::keyPressEvent(QKeyEvent *event)
1968 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
1972 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
1973 d->setCurrentNextEnabledIndex(offset);
1978 #ifndef QT_NO_WHEELEVENT
1979 void QTabBar::wheelEvent(QWheelEvent *event)
1982 int offset = event->delta() > 0 ? -1 : 1;
1983 d->setCurrentNextEnabledIndex(offset);
1984 QWidget::wheelEvent(event);
1986 #endif //QT_NO_WHEELEVENT
1988 void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
1991 for (int index = currentIndex + offset; validIndex(index); index += offset) {
1992 if (tabList.at(index).enabled) {
1993 q->setCurrentIndex(index);
2001 void QTabBar::changeEvent(QEvent *event)
2004 if (event->type() == QEvent::StyleChange) {
2005 if (!d->elideModeSetByUser)
2006 d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
2007 if (!d->useScrollButtonsSetByUser)
2008 d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
2010 } else if (event->type() == QEvent::FontChange) {
2013 QWidget::changeEvent(event);
2017 \property QTabBar::elideMode
2018 \brief how to elide text in the tab bar
2021 This property controls how items are elided when there is not
2022 enough space to show them for a given tab bar size.
2024 By default the value is style dependent.
2026 \sa QTabWidget::elideMode usesScrollButtons QStyle::SH_TabBar_ElideMode
2029 Qt::TextElideMode QTabBar::elideMode() const
2032 return d->elideMode;
2035 void QTabBar::setElideMode(Qt::TextElideMode mode)
2038 d->elideMode = mode;
2039 d->elideModeSetByUser = true;
2044 \property QTabBar::usesScrollButtons
2045 \brief Whether or not a tab bar should use buttons to scroll tabs when it
2049 When there are too many tabs in a tab bar for its size, the tab bar can either choose
2050 to expand its size or to add buttons that allow you to scroll through the tabs.
2052 By default the value is style dependant.
2054 \sa elideMode QTabWidget::usesScrollButtons QStyle::SH_TabBar_PreferNoArrows
2056 bool QTabBar::usesScrollButtons() const
2058 return d_func()->useScrollButtons;
2061 void QTabBar::setUsesScrollButtons(bool useButtons)
2064 d->useScrollButtonsSetByUser = true;
2065 if (d->useScrollButtons == useButtons)
2067 d->useScrollButtons = useButtons;
2072 \fn void QTabBar::setCurrentTab(int index)
2074 Use setCurrentIndex() instead.
2078 \fn void QTabBar::selected(int index);
2080 Use currentChanged() instead.
2085 \property QTabBar::tabsClosable
2086 \brief Whether or not a tab bar should place close buttons on each tab
2089 When tabsClosable is set to true a close button will appear on the tab on
2090 either the left or right hand side depending upon the style. When the button
2091 is clicked the tab the signal tabCloseRequested will be emitted.
2093 By default the value is false.
2095 \sa setTabButton(), tabRemoved()
2098 bool QTabBar::tabsClosable() const
2101 return d->closeButtonOnTabs;
2104 void QTabBar::setTabsClosable(bool closable)
2107 if (d->closeButtonOnTabs == closable)
2109 d->closeButtonOnTabs = closable;
2110 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
2112 for (int i = 0; i < d->tabList.count(); ++i) {
2113 if (closeSide == LeftSide && d->tabList[i].leftWidget) {
2114 d->tabList[i].leftWidget->deleteLater();
2115 d->tabList[i].leftWidget = 0;
2117 if (closeSide == RightSide && d->tabList[i].rightWidget) {
2118 d->tabList[i].rightWidget->deleteLater();
2119 d->tabList[i].rightWidget = 0;
2123 bool newButtons = false;
2124 for (int i = 0; i < d->tabList.count(); ++i) {
2125 if (tabButton(i, closeSide))
2128 QAbstractButton *closeButton = new CloseButton(this);
2129 connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
2130 setTabButton(i, closeSide, closeButton);
2139 \enum QTabBar::ButtonPosition
2142 This enum type lists the location of the widget on a tab.
2144 \value LeftSide Left side of the tab.
2146 \value RightSide Right side of the tab.
2151 \enum QTabBar::SelectionBehavior
2154 This enum type lists the behavior of QTabBar when a tab is removed
2155 and the tab being removed is also the current tab.
2157 \value SelectLeftTab Select the tab to the left of the one being removed.
2159 \value SelectRightTab Select the tab to the right of the one being removed.
2161 \value SelectPreviousTab Select the previously selected tab.
2166 \property QTabBar::selectionBehaviorOnRemove
2167 \brief What tab should be set as current when removeTab is called if
2168 the removed tab is also the current tab.
2171 By default the value is SelectRightTab.
2177 QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2180 return d->selectionBehaviorOnRemove;
2183 void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2186 d->selectionBehaviorOnRemove = behavior;
2190 \property QTabBar::expanding
2191 \brief When expanding is true QTabBar will expand the tabs to use the empty space.
2194 By default the value is true.
2196 \sa QTabWidget::documentMode
2199 bool QTabBar::expanding() const
2202 return d->expanding;
2205 void QTabBar::setExpanding(bool enabled)
2208 if (d->expanding == enabled)
2210 d->expanding = enabled;
2215 \property QTabBar::movable
2216 \brief This property holds whether the user can move the tabs
2217 within the tabbar area.
2221 By default, this property is false;
2224 bool QTabBar::isMovable() const
2230 void QTabBar::setMovable(bool movable)
2233 d->movable = movable;
2238 \property QTabBar::documentMode
2239 \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
2242 This property is used as a hint for styles to draw the tabs in a different
2243 way then they would normally look in a tab widget. On Mac OS X this will
2244 look similar to the tabs in Safari or Leopard's Terminal.app.
2246 \sa QTabWidget::documentMode
2248 bool QTabBar::documentMode() const
2250 return d_func()->documentMode;
2253 void QTabBar::setDocumentMode(bool enabled)
2257 d->documentMode = enabled;
2258 d->updateMacBorderMetrics();
2262 Sets \a widget on the tab \a index. The widget is placed
2263 on the left or right hand side depending upon the \a position.
2266 Any previously set widget in \a position is hidden.
2268 The tab bar will take ownership of the widget and so all widgets set here
2269 will be deleted by the tab bar when it is destroyed unless you separately
2270 reparent the widget after setting some other widget (or 0).
2274 void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
2277 if (index < 0 || index >= d->tabList.count())
2280 widget->setParent(this);
2281 // make sure our left and right widgets stay on top
2285 if (position == LeftSide) {
2286 if (d->tabList[index].leftWidget)
2287 d->tabList[index].leftWidget->hide();
2288 d->tabList[index].leftWidget = widget;
2290 if (d->tabList[index].rightWidget)
2291 d->tabList[index].rightWidget->hide();
2292 d->tabList[index].rightWidget = widget;
2300 Returns the widget set a tab \a index and \a position or 0 if
2303 QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
2306 if (index < 0 || index >= d->tabList.count())
2308 if (position == LeftSide)
2309 return d->tabList.at(index).leftWidget;
2311 return d->tabList.at(index).rightWidget;
2314 CloseButton::CloseButton(QWidget *parent)
2315 : QAbstractButton(parent)
2317 setFocusPolicy(Qt::NoFocus);
2318 #ifndef QT_NO_CURSOR
2319 setCursor(Qt::ArrowCursor);
2321 #ifndef QT_NO_TOOLTIP
2322 setToolTip(tr("Close Tab"));
2327 QSize CloseButton::sizeHint() const
2330 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
2331 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
2332 return QSize(width, height);
2335 void CloseButton::enterEvent(QEvent *event)
2339 QAbstractButton::enterEvent(event);
2342 void CloseButton::leaveEvent(QEvent *event)
2346 QAbstractButton::leaveEvent(event);
2349 void CloseButton::paintEvent(QPaintEvent *)
2354 opt.state |= QStyle::State_AutoRaise;
2355 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2356 opt.state |= QStyle::State_Raised;
2358 opt.state |= QStyle::State_On;
2360 opt.state |= QStyle::State_Sunken;
2362 if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
2363 int index = tb->currentIndex();
2364 QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
2365 if (tb->tabButton(index, position) == this)
2366 opt.state |= QStyle::State_Selected;
2369 style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
2374 #include "moc_qtabbar.cpp"
2376 #endif // QT_NO_TABBAR