source: trunk/src/gui/widgets/qtabbar.cpp@ 603

Last change on this file since 603 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 69.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qlayoutengine_p.h"
43#include "qabstractitemdelegate.h"
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#include "qtabwidget.h"
53#include "qtooltip.h"
54#include "qwhatsthis.h"
55#include "private/qtextengine_p.h"
56#ifndef QT_NO_ACCESSIBILITY
57#include "qaccessible.h"
58#endif
59
60#include "qdebug.h"
61#include "private/qtabbar_p.h"
62
63#ifndef QT_NO_TABBAR
64
65#ifdef Q_WS_MAC
66#include <private/qt_mac_p.h>
67#include <private/qt_cocoa_helpers_mac_p.h>
68#endif
69
70QT_BEGIN_NAMESPACE
71
72inline static bool verticalTabs(QTabBar::Shape shape)
73{
74 return shape == QTabBar::RoundedWest
75 || shape == QTabBar::RoundedEast
76 || shape == QTabBar::TriangularWest
77 || shape == QTabBar::TriangularEast;
78}
79
80void QTabBarPrivate::updateMacBorderMetrics()
81{
82#if (defined Q_WS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
83 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
84 Q_Q(QTabBar);
85 ::HIContentBorderMetrics metrics;
86
87 // TODO: get metrics to preserve the bottom value
88 // TODO: test tab bar position
89
90 OSWindowRef window = qt_mac_window_for(q);
91
92 // push base line separator down to the client are so we can paint over it (Carbon)
93 metrics.top = (documentMode && q->isVisible()) ? 1 : 0;
94 metrics.bottom = 0;
95 metrics.left = 0;
96 metrics.right = 0;
97 qt_mac_updateContentBorderMetricts(window, metrics);
98
99 // hide the base line separator if the tabs have docuemnt mode enabled (Cocoa)
100 qt_mac_showBaseLineSeparator(window, !documentMode);
101 }
102#endif
103}
104
105/*!
106 Initialize \a option with the values from the tab at \a tabIndex. This method
107 is useful for subclasses when they need a QStyleOptionTab, QStyleOptionTabV2,
108 or QStyleOptionTabV3 but don't want to fill in all the information themselves.
109 This function will check the version of the QStyleOptionTab and fill in the
110 additional values for a QStyleOptionTabV2 and QStyleOptionTabV3.
111
112 \sa QStyleOption::initFrom() QTabWidget::initStyleOption()
113*/
114void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
115{
116 Q_D(const QTabBar);
117 int totalTabs = d->tabList.size();
118
119 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
120 return;
121
122 const QTabBarPrivate::Tab &tab = d->tabList.at(tabIndex);
123 option->initFrom(this);
124 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
125 option->rect = tabRect(tabIndex);
126 bool isCurrent = tabIndex == d->currentIndex;
127 option->row = 0;
128 if (tabIndex == d->pressedIndex)
129 option->state |= QStyle::State_Sunken;
130 if (isCurrent)
131 option->state |= QStyle::State_Selected;
132 if (isCurrent && hasFocus())
133 option->state |= QStyle::State_HasFocus;
134 if (!tab.enabled)
135 option->state &= ~QStyle::State_Enabled;
136 if (isActiveWindow())
137 option->state |= QStyle::State_Active;
138 if (!d->dragInProgress && option->rect == d->hoverRect)
139 option->state |= QStyle::State_MouseOver;
140 option->shape = d->shape;
141 option->text = tab.text;
142
143 if (tab.textColor.isValid())
144 option->palette.setColor(foregroundRole(), tab.textColor);
145
146 option->icon = tab.icon;
147 if (QStyleOptionTabV2 *optionV2 = qstyleoption_cast<QStyleOptionTabV2 *>(option))
148 optionV2->iconSize = iconSize(); // Will get the default value then.
149
150 if (QStyleOptionTabV3 *optionV3 = qstyleoption_cast<QStyleOptionTabV3 *>(option)) {
151 optionV3->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
152 optionV3->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
153 optionV3->documentMode = d->documentMode;
154 }
155
156 if (tabIndex > 0 && tabIndex - 1 == d->currentIndex)
157 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
158 else if (tabIndex < totalTabs - 1 && tabIndex + 1 == d->currentIndex)
159 option->selectedPosition = QStyleOptionTab::NextIsSelected;
160 else
161 option->selectedPosition = QStyleOptionTab::NotAdjacent;
162
163 bool paintBeginning = (tabIndex == 0) || (d->dragInProgress && tabIndex == d->pressedIndex + 1);
164 bool paintEnd = (tabIndex == totalTabs - 1) || (d->dragInProgress && tabIndex == d->pressedIndex - 1);
165 if (paintBeginning) {
166 if (paintEnd)
167 option->position = QStyleOptionTab::OnlyOneTab;
168 else
169 option->position = QStyleOptionTab::Beginning;
170 } else if (paintEnd) {
171 option->position = QStyleOptionTab::End;
172 } else {
173 option->position = QStyleOptionTab::Middle;
174 }
175
176#ifndef QT_NO_TABWIDGET
177 if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(parentWidget())) {
178 if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
179 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
180 if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
181 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
182 }
183#endif
184
185 QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
186 option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
187 Qt::TextShowMnemonic);
188}
189
190/*!
191 \class QTabBar
192 \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
193
194 \ingroup basicwidgets
195
196
197 QTabBar is straightforward to use; it draws the tabs using one of
198 the predefined \link QTabBar::Shape shapes\endlink, and emits a
199 signal when a tab is selected. It can be subclassed to tailor the
200 look and feel. Qt also provides a ready-made \l{QTabWidget}.
201
202 Each tab has a tabText(), an optional tabIcon(), an optional
203 tabToolTip(), optional tabWhatsThis() and optional tabData().
204 The tabs's attributes can be changed with setTabText(), setTabIcon(),
205 setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
206 enabled or disabled individually with setTabEnabled().
207
208 Each tab can display text in a distinct color. The current text color
209 for a tab can be found with the tabTextColor() function. Set the text
210 color for a particular tab with setTabTextColor().
211
212 Tabs are added using addTab(), or inserted at particular positions
213 using insertTab(). The total number of tabs is given by
214 count(). Tabs can be removed from the tab bar with
215 removeTab(). Combining removeTab() and insertTab() allows you to
216 move tabs to different positions.
217
218 The \l shape property defines the tabs' appearance. The choice of
219 shape is a matter of taste, although tab dialogs (for preferences
220 and similar) invariably use \l RoundedNorth.
221 Tab controls in windows other than dialogs almost
222 always use either \l RoundedSouth or \l TriangularSouth. Many
223 spreadsheets and other tab controls in which all the pages are
224 essentially similar use \l TriangularSouth, whereas \l
225 RoundedSouth is used mostly when the pages are different (e.g. a
226 multi-page tool palette). The default in QTabBar is \l
227 RoundedNorth.
228
229 The most important part of QTabBar's API is the currentChanged()
230 signal. This is emitted whenever the current tab changes (even at
231 startup, when the current tab changes from 'none'). There is also
232 a slot, setCurrentIndex(), which can be used to select a tab
233 programmatically. The function currentIndex() returns the index of
234 the current tab, \l count holds the number of tabs.
235
236 QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
237 e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
238 key for switching to that tab.
239
240 The following virtual functions may need to be reimplemented in
241 order to tailor the look and feel or store extra data with each
242 tab:
243
244 \list
245 \i tabSizeHint() calcuates the size of a tab.
246 \i tabInserted() notifies that a new tab was added.
247 \i tabRemoved() notifies that a tab was removed.
248 \i tabLayoutChange() notifies that the tabs have been re-laid out.
249 \i paintEvent() paints all tabs.
250 \endlist
251
252 For subclasses, you might also need the tabRect() functions which
253 returns the visual geometry of a single tab.
254
255 \table 100%
256 \row \o \inlineimage plastique-tabbar.png Screenshot of a Plastique style tab bar
257 \o A tab bar shown in the Plastique widget style.
258 \row \o \inlineimage plastique-tabbar-truncated.png Screenshot of a truncated Plastique tab bar
259 \o A truncated tab bar shown in the Plastique widget style.
260 \endtable
261
262 \sa QTabWidget
263*/
264
265/*!
266 \enum QTabBar::Shape
267
268 This enum type lists the built-in shapes supported by QTabBar. Treat these
269 as hints as some styles may not render some of the shapes. However,
270 position should be honored.
271
272 \value RoundedNorth The normal rounded look above the pages
273
274 \value RoundedSouth The normal rounded look below the pages
275
276 \value RoundedWest The normal rounded look on the left side of the pages
277
278 \value RoundedEast The normal rounded look on the right side the pages
279
280 \value TriangularNorth Triangular tabs above the pages.
281
282 \value TriangularSouth Triangular tabs similar to those used in
283 the Excel spreadsheet, for example
284
285 \value TriangularWest Triangular tabs on the left of the pages.
286
287 \value TriangularEast Triangular tabs on the right of the pages.
288 \omitvalue RoundedAbove
289 \omitvalue RoundedBelow
290 \omitvalue TriangularAbove
291 \omitvalue TriangularBelow
292*/
293
294/*!
295 \fn void QTabBar::currentChanged(int index)
296
297 This signal is emitted when the tab bar's current tab changes. The
298 new current has the given \a index, or -1 if there isn't a new one
299 (for example, if there are no tab in the QTabBar)
300*/
301
302/*!
303 \fn void QTabBar::tabCloseRequested(int index)
304 \since 4.5
305
306 This signal is emitted when the close button on a tab is clicked.
307 The \a index is the index that should be removed.
308
309 \sa setTabsClosable()
310*/
311
312/*!
313 \fn void QTabBar::tabMoved(int from, int to)
314 \since 4.5
315
316 This signal is emitted when the tab has moved the tab
317 at index position \a from to index position \a to.
318
319 note: QTabWidget will automatically move the page when
320 this signal is emitted from its tab bar.
321
322 \sa moveTab()
323*/
324
325int QTabBarPrivate::extraWidth() const
326{
327 Q_Q(const QTabBar);
328 return 2 * qMax(q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, q),
329 QApplication::globalStrut().width());
330}
331
332void QTabBarPrivate::init()
333{
334 Q_Q(QTabBar);
335 leftB = new QToolButton(q);
336 leftB->setAutoRepeat(true);
337 QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
338 leftB->hide();
339 rightB = new QToolButton(q);
340 rightB->setAutoRepeat(true);
341 QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
342 rightB->hide();
343#ifdef QT_KEYPAD_NAVIGATION
344 if (QApplication::keypadNavigationEnabled()) {
345 leftB->setFocusPolicy(Qt::NoFocus);
346 rightB->setFocusPolicy(Qt::NoFocus);
347 q->setFocusPolicy(Qt::NoFocus);
348 } else
349#endif
350 q->setFocusPolicy(Qt::TabFocus);
351 q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
352 elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
353 useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
354}
355
356QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
357{
358 return validIndex(index)?&tabList[index]:0;
359}
360
361const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
362{
363 return validIndex(index)?&tabList[index]:0;
364}
365
366int QTabBarPrivate::indexAtPos(const QPoint &p) const
367{
368 Q_Q(const QTabBar);
369 if (q->tabRect(currentIndex).contains(p))
370 return currentIndex;
371 for (int i = 0; i < tabList.count(); ++i)
372 if (tabList.at(i).enabled && q->tabRect(i).contains(p))
373 return i;
374 return -1;
375}
376
377void QTabBarPrivate::layoutTabs()
378{
379 Q_Q(QTabBar);
380 scrollOffset = 0;
381 layoutDirty = false;
382 QSize size = q->size();
383 int last, available;
384 int maxExtent;
385 int i;
386 bool vertTabs = verticalTabs(shape);
387 int tabChainIndex = 0;
388
389 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
390 QVector<QLayoutStruct> tabChain(tabList.count() + 2);
391
392 // We put an empty item at the front and back and set its expansive attribute
393 // depending on tabAlignment.
394 tabChain[tabChainIndex].init();
395 tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignLeft)
396 && (tabAlignment != Qt::AlignJustify);
397 tabChain[tabChainIndex].empty = true;
398 ++tabChainIndex;
399
400 // We now go through our list of tabs and set the minimum size and the size hint
401 // This will allow us to elide text if necessary. Since we don't set
402 // a maximum size, tabs will EXPAND to fill up the empty space.
403 // Since tab widget is rather *ahem* strict about keeping the geometry of the
404 // tab bar to its absolute minimum, this won't bleed through, but will show up
405 // if you use tab bar on its own (a.k.a. not a bug, but a feature).
406 // Update: if expanding is false, we DO set a maximum size to prevent the tabs
407 // being wider than necessary.
408 if (!vertTabs) {
409 int minx = 0;
410 int x = 0;
411 int maxHeight = 0;
412 for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
413 QSize sz = q->tabSizeHint(i);
414 tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
415 x += sz.width();
416 maxHeight = qMax(maxHeight, sz.height());
417 sz = minimumTabSizeHint(i);
418 tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
419 minx += sz.width();
420 tabChain[tabChainIndex].init();
421 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
422 tabChain[tabChainIndex].minimumSize = sz.width();
423 tabChain[tabChainIndex].empty = false;
424 tabChain[tabChainIndex].expansive = true;
425
426 if (!expanding)
427 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
428 }
429
430 last = minx;
431 available = size.width();
432 maxExtent = maxHeight;
433 } else {
434 int miny = 0;
435 int y = 0;
436 int maxWidth = 0;
437 for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
438 QSize sz = q->tabSizeHint(i);
439 tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
440 y += sz.height();
441 maxWidth = qMax(maxWidth, sz.width());
442 sz = minimumTabSizeHint(i);
443 tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
444 miny += sz.height();
445 tabChain[tabChainIndex].init();
446 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
447 tabChain[tabChainIndex].minimumSize = sz.height();
448 tabChain[tabChainIndex].empty = false;
449 tabChain[tabChainIndex].expansive = true;
450
451 if (!expanding)
452 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
453 }
454
455 last = miny;
456 available = size.height();
457 maxExtent = maxWidth;
458 }
459
460 Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
461 // Mirror our front item.
462 tabChain[tabChainIndex].init();
463 tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
464 && (tabAlignment != Qt::AlignJustify);
465 tabChain[tabChainIndex].empty = true;
466
467 // Do the calculation
468 qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
469
470 // Use the results
471 for (i = 0; i < tabList.count(); ++i) {
472 const QLayoutStruct &lstruct = tabChain.at(i + 1);
473 if (!vertTabs)
474 tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
475 else
476 tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
477 }
478
479 if (useScrollButtons && tabList.count() && last > available) {
480 int extra = extraWidth();
481 if (!vertTabs) {
482 Qt::LayoutDirection ld = q->layoutDirection();
483 QRect arrows = QStyle::visualRect(ld, q->rect(),
484 QRect(available - extra, 0, extra, size.height()));
485 int buttonOverlap = q->style()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, q);
486
487 if (ld == Qt::LeftToRight) {
488 leftB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
489 rightB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
490 extra/2, arrows.height());
491 leftB->setArrowType(Qt::LeftArrow);
492 rightB->setArrowType(Qt::RightArrow);
493 } else {
494 rightB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
495 leftB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
496 extra/2, arrows.height());
497 rightB->setArrowType(Qt::LeftArrow);
498 leftB->setArrowType(Qt::RightArrow);
499 }
500 } else {
501 QRect arrows = QRect(0, available - extra, size.width(), extra );
502 leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra/2);
503 leftB->setArrowType(Qt::UpArrow);
504 rightB->setGeometry(arrows.left(), arrows.bottom() - extra/2 + 1,
505 arrows.width(), extra/2);
506 rightB->setArrowType(Qt::DownArrow);
507 }
508 leftB->setEnabled(scrollOffset > 0);
509 rightB->setEnabled(last - scrollOffset >= available - extra);
510 leftB->show();
511 rightB->show();
512 } else {
513 rightB->hide();
514 leftB->hide();
515 }
516
517 layoutWidgets();
518 q->tabLayoutChange();
519}
520
521void QTabBarPrivate::makeVisible(int index)
522{
523 Q_Q(QTabBar);
524 if (!validIndex(index) || leftB->isHidden())
525 return;
526
527 const QRect tabRect = tabList.at(index).rect;
528 const int oldScrollOffset = scrollOffset;
529 const bool horiz = !verticalTabs(shape);
530 const int available = (horiz ? q->width() : q->height()) - extraWidth();
531 const int start = horiz ? tabRect.left() : tabRect.top();
532 const int end = horiz ? tabRect.right() : tabRect.bottom();
533 if (start < scrollOffset) // too far left
534 scrollOffset = start - (index ? 8 : 0);
535 else if (end > scrollOffset + available) // too far right
536 scrollOffset = end - available + 1;
537
538 leftB->setEnabled(scrollOffset > 0);
539 const int last = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom();
540 rightB->setEnabled(last - scrollOffset >= available);
541 if (oldScrollOffset != scrollOffset) {
542 q->update();
543 layoutWidgets();
544 }
545}
546
547void QTabBarPrivate::layoutTab(int index)
548{
549 Q_Q(QTabBar);
550 Q_ASSERT(index >= 0);
551
552 Tab &tab = tabList[index];
553 bool vertical = verticalTabs(shape);
554 if (!(tab.leftWidget || tab.rightWidget))
555 return;
556
557 QStyleOptionTabV3 opt;
558 q->initStyleOption(&opt, index);
559 if (tab.leftWidget) {
560 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
561 QPoint p = rect.topLeft();
562 if ((index == pressedIndex) || paintWithOffsets) {
563 if (vertical)
564 p.setY(p.y() + tabList[index].dragOffset);
565 else
566 p.setX(p.x() + tabList[index].dragOffset);
567 }
568 tab.leftWidget->move(p);
569 }
570 if (tab.rightWidget) {
571 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
572 QPoint p = rect.topLeft();
573 if ((index == pressedIndex) || paintWithOffsets) {
574 if (vertical)
575 p.setY(p.y() + tab.dragOffset);
576 else
577 p.setX(p.x() + tab.dragOffset);
578 }
579 tab.rightWidget->move(p);
580 }
581}
582
583void QTabBarPrivate::layoutWidgets(int index)
584{
585 Q_Q(QTabBar);
586 int start = 0;
587 int end = q->count();
588 if (index != -1) {
589 start = qMax(index, 0);
590 end = qMin(end, start + 1);
591 }
592 for (int i = start; i < end; ++i) {
593 layoutTab(i);
594 }
595}
596
597void QTabBarPrivate::_q_closeTab()
598{
599 Q_Q(QTabBar);
600 QObject *object = q->sender();
601 int tabToClose = -1;
602 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, q);
603 for (int i = 0; i < tabList.count(); ++i) {
604 if (closeSide == QTabBar::LeftSide) {
605 if (tabList.at(i).leftWidget == object) {
606 tabToClose = i;
607 break;
608 }
609 } else {
610 if (tabList.at(i).rightWidget == object) {
611 tabToClose = i;
612 break;
613 }
614 }
615 }
616 if (tabToClose != -1)
617 emit q->tabCloseRequested(tabToClose);
618}
619
620void QTabBarPrivate::_q_scrollTabs()
621{
622 Q_Q(QTabBar);
623 const QObject *sender = q->sender();
624 int i = -1;
625 if (!verticalTabs(shape)) {
626 if (sender == leftB) {
627 for (i = tabList.count() - 1; i >= 0; --i) {
628 if (tabList.at(i).rect.left() - scrollOffset < 0) {
629 makeVisible(i);
630 return;
631 }
632 }
633 } else if (sender == rightB) {
634 int availableWidth = q->width() - extraWidth();
635 for (i = 0; i < tabList.count(); ++i) {
636 if (tabList.at(i).rect.right() - scrollOffset > availableWidth) {
637 makeVisible(i);
638 return;
639 }
640 }
641 }
642 } else { // vertical
643 if (sender == leftB) {
644 for (i = tabList.count() - 1; i >= 0; --i) {
645 if (tabList.at(i).rect.top() - scrollOffset < 0) {
646 makeVisible(i);
647 return;
648 }
649 }
650 } else if (sender == rightB) {
651 int available = q->height() - extraWidth();
652 for (i = 0; i < tabList.count(); ++i) {
653 if (tabList.at(i).rect.bottom() - scrollOffset > available) {
654 makeVisible(i);
655 return;
656 }
657 }
658 }
659 }
660}
661
662void QTabBarPrivate::refresh()
663{
664 Q_Q(QTabBar);
665
666 // be safe in case a subclass is also handling move with the tabs
667 if (pressedIndex != -1
668 && movable
669 && QApplication::mouseButtons() == Qt::NoButton) {
670 moveTabFinished(pressedIndex);
671 if (!validIndex(pressedIndex))
672 pressedIndex = -1;
673 }
674
675 if (!q->isVisible()) {
676 layoutDirty = true;
677 } else {
678 layoutTabs();
679 makeVisible(currentIndex);
680 q->update();
681 q->updateGeometry();
682 }
683}
684
685/*!
686 Creates a new tab bar with the given \a parent.
687*/
688QTabBar::QTabBar(QWidget* parent)
689 :QWidget(*new QTabBarPrivate, parent, 0)
690{
691 Q_D(QTabBar);
692 d->init();
693}
694
695
696/*!
697 Destroys the tab bar.
698*/
699QTabBar::~QTabBar()
700{
701}
702
703/*!
704 \property QTabBar::shape
705 \brief the shape of the tabs in the tab bar
706
707 Possible values for this property are described by the Shape enum.
708*/
709
710
711QTabBar::Shape QTabBar::shape() const
712{
713 Q_D(const QTabBar);
714 return d->shape;
715}
716
717void QTabBar::setShape(Shape shape)
718{
719 Q_D(QTabBar);
720 if (d->shape == shape)
721 return;
722 d->shape = shape;
723 d->refresh();
724}
725
726/*!
727 \property QTabBar::drawBase
728 \brief defines whether or not tab bar should draw its base.
729
730 If true then QTabBar draws a base in relation to the styles overlab.
731 Otherwise only the tabs are drawn.
732
733 \sa QStyle::pixelMetric() QStyle::PM_TabBarBaseOverlap QStyleOptionTabBarBaseV2
734*/
735
736void QTabBar::setDrawBase(bool drawBase)
737{
738 Q_D(QTabBar);
739 if (d->drawBase == drawBase)
740 return;
741 d->drawBase = drawBase;
742 update();
743}
744
745bool QTabBar::drawBase() const
746{
747 Q_D(const QTabBar);
748 return d->drawBase;
749}
750
751/*!
752 Adds a new tab with text \a text. Returns the new
753 tab's index.
754*/
755int QTabBar::addTab(const QString &text)
756{
757 return insertTab(-1, text);
758}
759
760/*!
761 \overload
762
763 Adds a new tab with icon \a icon and text \a
764 text. Returns the new tab's index.
765*/
766int QTabBar::addTab(const QIcon& icon, const QString &text)
767{
768 return insertTab(-1, icon, text);
769}
770
771/*!
772 Inserts a new tab with text \a text at position \a index. If \a
773 index is out of range, the new tab is appened. Returns the new
774 tab's index.
775*/
776int QTabBar::insertTab(int index, const QString &text)
777{
778 return insertTab(index, QIcon(), text);
779}
780
781/*!\overload
782
783 Inserts a new tab with icon \a icon and text \a text at position
784 \a index. If \a index is out of range, the new tab is
785 appended. Returns the new tab's index.
786
787 If the QTabBar was empty before this function is called, the inserted tab
788 becomes the current tab.
789
790 Inserting a new tab at an index less than or equal to the current index
791 will increment the current index, but keep the current tab.
792*/
793int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
794{
795 Q_D(QTabBar);
796 if (!d->validIndex(index)) {
797 index = d->tabList.count();
798 d->tabList.append(QTabBarPrivate::Tab(icon, text));
799 } else {
800 d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
801 }
802#ifndef QT_NO_SHORTCUT
803 d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
804#endif
805 d->refresh();
806 if (d->tabList.count() == 1)
807 setCurrentIndex(index);
808 else if (index <= d->currentIndex)
809 ++d->currentIndex;
810
811 if (d->closeButtonOnTabs) {
812 QStyleOptionTabV3 opt;
813 initStyleOption(&opt, index);
814 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
815 QAbstractButton *closeButton = new CloseButton(this);
816 connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
817 setTabButton(index, closeSide, closeButton);
818 }
819
820 for (int i = 0; i < d->tabList.count(); ++i) {
821 if (d->tabList[i].lastTab >= index)
822 ++d->tabList[i].lastTab;
823 }
824
825 tabInserted(index);
826 return index;
827}
828
829
830/*!
831 Removes the tab at position \a index.
832
833 \sa SelectionBehavior
834 */
835void QTabBar::removeTab(int index)
836{
837 Q_D(QTabBar);
838 if (d->validIndex(index)) {
839#ifndef QT_NO_SHORTCUT
840 releaseShortcut(d->tabList.at(index).shortcutId);
841#endif
842 if (d->tabList[index].leftWidget) {
843 d->tabList[index].leftWidget->hide();
844 d->tabList[index].leftWidget->deleteLater();
845 d->tabList[index].leftWidget = 0;
846 }
847 if (d->tabList[index].rightWidget) {
848 d->tabList[index].rightWidget->hide();
849 d->tabList[index].rightWidget->deleteLater();
850 d->tabList[index].rightWidget = 0;
851 }
852
853 int newIndex = d->tabList[index].lastTab;
854 d->tabList.removeAt(index);
855 for (int i = 0; i < d->tabList.count(); ++i) {
856 if (d->tabList[i].lastTab == index)
857 d->tabList[i].lastTab = -1;
858 if (d->tabList[i].lastTab > index)
859 --d->tabList[i].lastTab;
860 }
861 if (index == d->currentIndex) {
862 // The current tab is going away, in order to make sure
863 // we emit that "current has changed", we need to reset this
864 // around.
865 d->currentIndex = -1;
866 if (d->tabList.size() > 0) {
867 switch(d->selectionBehaviorOnRemove) {
868 case SelectPreviousTab:
869 if (newIndex > index)
870 newIndex--;
871 if (d->validIndex(newIndex))
872 break;
873 // else fallthrough
874 case SelectRightTab:
875 newIndex = index;
876 if (newIndex >= d->tabList.size())
877 newIndex = d->tabList.size() - 1;
878 break;
879 case SelectLeftTab:
880 newIndex = index - 1;
881 if (newIndex < 0)
882 newIndex = 0;
883 break;
884 default:
885 break;
886 }
887
888 if (d->validIndex(newIndex)) {
889 // don't loose newIndex's old through setCurrentIndex
890 int bump = d->tabList[newIndex].lastTab;
891 setCurrentIndex(newIndex);
892 d->tabList[newIndex].lastTab = bump;
893 }
894 } else {
895 emit currentChanged(-1);
896 }
897 } else if (index < d->currentIndex) {
898 setCurrentIndex(d->currentIndex - 1);
899 }
900 d->refresh();
901 tabRemoved(index);
902 }
903}
904
905
906/*!
907 Returns true if the tab at position \a index is enabled; otherwise
908 returns false.
909*/
910bool QTabBar::isTabEnabled(int index) const
911{
912 Q_D(const QTabBar);
913 if (const QTabBarPrivate::Tab *tab = d->at(index))
914 return tab->enabled;
915 return false;
916}
917
918/*!
919 If \a enabled is true then the tab at position \a index is
920 enabled; otherwise the item at position \a index is disabled.
921*/
922void QTabBar::setTabEnabled(int index, bool enabled)
923{
924 Q_D(QTabBar);
925 if (QTabBarPrivate::Tab *tab = d->at(index)) {
926 tab->enabled = enabled;
927#ifndef QT_NO_SHORTCUT
928 setShortcutEnabled(tab->shortcutId, enabled);
929#endif
930 update();
931 if (!enabled && index == d->currentIndex)
932 setCurrentIndex(d->validIndex(index+1)?index+1:0);
933 else if (enabled && !d->validIndex(d->currentIndex))
934 setCurrentIndex(index);
935 }
936}
937
938
939/*!
940 Returns the text of the tab at position \a index, or an empty
941 string if \a index is out of range.
942*/
943QString QTabBar::tabText(int index) const
944{
945 Q_D(const QTabBar);
946 if (const QTabBarPrivate::Tab *tab = d->at(index))
947 return tab->text;
948 return QString();
949}
950
951/*!
952 Sets the text of the tab at position \a index to \a text.
953*/
954void QTabBar::setTabText(int index, const QString &text)
955{
956 Q_D(QTabBar);
957 if (QTabBarPrivate::Tab *tab = d->at(index)) {
958 tab->text = text;
959#ifndef QT_NO_SHORTCUT
960 releaseShortcut(tab->shortcutId);
961 tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
962 setShortcutEnabled(tab->shortcutId, tab->enabled);
963#endif
964 d->refresh();
965 }
966}
967
968/*!
969 Returns the text color of the tab with the given \a index, or a invalid
970 color if \a index is out of range.
971
972 \sa setTabTextColor()
973*/
974QColor QTabBar::tabTextColor(int index) const
975{
976 Q_D(const QTabBar);
977 if (const QTabBarPrivate::Tab *tab = d->at(index))
978 return tab->textColor;
979 return QColor();
980}
981
982/*!
983 Sets the color of the text in the tab with the given \a index to the specified \a color.
984
985 If an invalid color is specified, the tab will use the QTabBar foreground role instead.
986
987 \sa tabTextColor()
988*/
989void QTabBar::setTabTextColor(int index, const QColor &color)
990{
991 Q_D(QTabBar);
992 if (QTabBarPrivate::Tab *tab = d->at(index)) {
993 tab->textColor = color;
994 update(tabRect(index));
995 }
996}
997
998/*!
999 Returns the icon of the tab at position \a index, or a null icon
1000 if \a index is out of range.
1001*/
1002QIcon QTabBar::tabIcon(int index) const
1003{
1004 Q_D(const QTabBar);
1005 if (const QTabBarPrivate::Tab *tab = d->at(index))
1006 return tab->icon;
1007 return QIcon();
1008}
1009
1010/*!
1011 Sets the icon of the tab at position \a index to \a icon.
1012*/
1013void QTabBar::setTabIcon(int index, const QIcon & icon)
1014{
1015 Q_D(QTabBar);
1016 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1017 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1018 tab->icon = icon;
1019 if (simpleIconChange)
1020 update(tabRect(index));
1021 else
1022 d->refresh();
1023 }
1024}
1025
1026#ifndef QT_NO_TOOLTIP
1027/*!
1028 Sets the tool tip of the tab at position \a index to \a tip.
1029*/
1030void QTabBar::setTabToolTip(int index, const QString & tip)
1031{
1032 Q_D(QTabBar);
1033 if (QTabBarPrivate::Tab *tab = d->at(index))
1034 tab->toolTip = tip;
1035}
1036
1037/*!
1038 Returns the tool tip of the tab at position \a index, or an empty
1039 string if \a index is out of range.
1040*/
1041QString QTabBar::tabToolTip(int index) const
1042{
1043 Q_D(const QTabBar);
1044 if (const QTabBarPrivate::Tab *tab = d->at(index))
1045 return tab->toolTip;
1046 return QString();
1047}
1048#endif // QT_NO_TOOLTIP
1049
1050#ifndef QT_NO_WHATSTHIS
1051/*!
1052 \since 4.1
1053
1054 Sets the What's This help text of the tab at position \a index
1055 to \a text.
1056*/
1057void QTabBar::setTabWhatsThis(int index, const QString &text)
1058{
1059 Q_D(QTabBar);
1060 if (QTabBarPrivate::Tab *tab = d->at(index))
1061 tab->whatsThis = text;
1062}
1063
1064/*!
1065 \since 4.1
1066
1067 Returns the What's This help text of the tab at position \a index,
1068 or an empty string if \a index is out of range.
1069*/
1070QString QTabBar::tabWhatsThis(int index) const
1071{
1072 Q_D(const QTabBar);
1073 if (const QTabBarPrivate::Tab *tab = d->at(index))
1074 return tab->whatsThis;
1075 return QString();
1076}
1077
1078#endif // QT_NO_WHATSTHIS
1079
1080/*!
1081 Sets the data of the tab at position \a index to \a data.
1082*/
1083void QTabBar::setTabData(int index, const QVariant & data)
1084{
1085 Q_D(QTabBar);
1086 if (QTabBarPrivate::Tab *tab = d->at(index))
1087 tab->data = data;
1088}
1089
1090/*!
1091 Returns the data of the tab at position \a index, or a null
1092 variant if \a index is out of range.
1093*/
1094QVariant QTabBar::tabData(int index) const
1095{
1096 Q_D(const QTabBar);
1097 if (const QTabBarPrivate::Tab *tab = d->at(index))
1098 return tab->data;
1099 return QVariant();
1100}
1101
1102/*!
1103 Returns the visual rectangle of the tab at position \a
1104 index, or a null rectangle if \a index is out of range.
1105*/
1106QRect QTabBar::tabRect(int index) const
1107{
1108 Q_D(const QTabBar);
1109 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1110 if (d->layoutDirty)
1111 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1112 QRect r = tab->rect;
1113 if (verticalTabs(d->shape))
1114 r.translate(0, -d->scrollOffset);
1115 else
1116 r.translate(-d->scrollOffset, 0);
1117 if (!verticalTabs(d->shape))
1118 r = QStyle::visualRect(layoutDirection(), rect(), r);
1119 return r;
1120 }
1121 return QRect();
1122}
1123
1124/*!
1125 \since 4.3
1126 Returns the index of the tab that covers \a position or -1 if no
1127 tab covers \a position;
1128*/
1129
1130int QTabBar::tabAt(const QPoint &position) const
1131{
1132 Q_D(const QTabBar);
1133 if (d->validIndex(d->currentIndex)
1134 && tabRect(d->currentIndex).contains(position)) {
1135 return d->currentIndex;
1136 }
1137 const int max = d->tabList.size();
1138 for (int i = 0; i < max; ++i) {
1139 if (tabRect(i).contains(position)) {
1140 return i;
1141 }
1142 }
1143 return -1;
1144}
1145
1146/*!
1147 \property QTabBar::currentIndex
1148 \brief the index of the tab bar's visible tab
1149
1150 The current index is -1 if there is no current tab.
1151*/
1152
1153int QTabBar::currentIndex() const
1154{
1155 Q_D(const QTabBar);
1156 if (d->validIndex(d->currentIndex))
1157 return d->currentIndex;
1158 return -1;
1159}
1160
1161
1162void QTabBar::setCurrentIndex(int index)
1163{
1164 Q_D(QTabBar);
1165 if (d->dragInProgress && d->pressedIndex != -1)
1166 return;
1167
1168 int oldIndex = d->currentIndex;
1169 if (d->validIndex(index) && d->currentIndex != index) {
1170 d->currentIndex = index;
1171 update();
1172 d->makeVisible(index);
1173 d->tabList[index].lastTab = oldIndex;
1174 d->layoutWidgets(oldIndex);
1175 d->layoutWidgets(index);
1176#ifdef QT3_SUPPORT
1177 emit selected(index);
1178#endif
1179 emit currentChanged(index);
1180 }
1181}
1182
1183/*!
1184 \property QTabBar::iconSize
1185 \brief The size for icons in the tab bar
1186 \since 4.1
1187
1188 The default value is style-dependent. \c iconSize is a maximum
1189 size; icons that are smaller are not scaled up.
1190
1191 \sa QTabWidget::iconSize
1192*/
1193QSize QTabBar::iconSize() const
1194{
1195 Q_D(const QTabBar);
1196 if (d->iconSize.isValid())
1197 return d->iconSize;
1198 int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
1199 return QSize(iconExtent, iconExtent);
1200
1201}
1202
1203void QTabBar::setIconSize(const QSize &size)
1204{
1205 Q_D(QTabBar);
1206 d->iconSize = size;
1207 d->layoutDirty = true;
1208 update();
1209 updateGeometry();
1210}
1211
1212/*!
1213 \property QTabBar::count
1214 \brief the number of tabs in the tab bar
1215*/
1216
1217int QTabBar::count() const
1218{
1219 Q_D(const QTabBar);
1220 return d->tabList.count();
1221}
1222
1223
1224/*!\reimp
1225 */
1226QSize QTabBar::sizeHint() const
1227{
1228 Q_D(const QTabBar);
1229 if (d->layoutDirty)
1230 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1231 QRect r;
1232 for (int i = 0; i < d->tabList.count(); ++i)
1233 r = r.united(d->tabList.at(i).maxRect);
1234 QSize sz = QApplication::globalStrut();
1235 return r.size().expandedTo(sz);
1236}
1237
1238/*!\reimp
1239 */
1240QSize QTabBar::minimumSizeHint() const
1241{
1242 Q_D(const QTabBar);
1243 if (!d->useScrollButtons) {
1244 QRect r;
1245 for (int i = 0; i < d->tabList.count(); ++i)
1246 r = r.united(d->tabList.at(i).minRect);
1247 return r.size().expandedTo(QApplication::globalStrut());
1248 }
1249 if (verticalTabs(d->shape))
1250 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1251 else
1252 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1253}
1254
1255static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
1256{
1257 if (text.length() <= 7)
1258 return text;
1259
1260 static const QLatin1String Ellipses("...");
1261 QString ret;
1262 switch (mode) {
1263 case Qt::ElideRight:
1264 ret = text.left(4) + Ellipses;
1265 break;
1266 case Qt::ElideMiddle:
1267 ret = text.left(2) + Ellipses + text.right(2);
1268 break;
1269 case Qt::ElideLeft:
1270 ret = Ellipses + text.right(4);
1271 break;
1272 case Qt::ElideNone:
1273 ret = text;
1274 break;
1275 }
1276 return ret;
1277}
1278
1279QSize QTabBarPrivate::minimumTabSizeHint(int index)
1280{
1281 Q_Q(QTabBar);
1282 // ### Qt 5: make this a protected virtual function in QTabBar
1283 Tab &tab = tabList[index];
1284 QString oldText = tab.text;
1285 tab.text = computeElidedText(elideMode, oldText);
1286 QSize size = q->tabSizeHint(index);
1287 tab.text = oldText;
1288 return size;
1289}
1290
1291/*!
1292 Returns the size hint for the tab at position \a index.
1293*/
1294QSize QTabBar::tabSizeHint(int index) const
1295{
1296 //Note: this must match with the computations in QCommonStylePrivate::tabLayout
1297 Q_D(const QTabBar);
1298 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1299 QStyleOptionTabV3 opt;
1300 initStyleOption(&opt, index);
1301 opt.text = d->tabList.at(index).text;
1302 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1303 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
1304 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
1305 const QFontMetrics fm = fontMetrics();
1306
1307 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1308 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1309
1310 int widgetWidth = 0;
1311 int widgetHeight = 0;
1312 int padding = 0;
1313 if (!opt.leftButtonSize.isEmpty()) {
1314 padding += 4;
1315 widgetWidth += opt.leftButtonSize.width();
1316 widgetHeight += opt.leftButtonSize.height();
1317 }
1318 if (!opt.rightButtonSize.isEmpty()) {
1319 padding += 4;
1320 widgetWidth += opt.rightButtonSize.width();
1321 widgetHeight += opt.rightButtonSize.height();
1322 }
1323 if (!opt.icon.isNull())
1324 padding += 4;
1325
1326 QSize csz;
1327 if (verticalTabs(d->shape)) {
1328 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1329 fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe + widgetHeight + padding);
1330 } else {
1331 csz = QSize(fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe
1332 + widgetWidth + padding,
1333 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1334 }
1335
1336 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
1337 return retSize;
1338 }
1339 return QSize();
1340}
1341
1342/*!
1343 This virtual handler is called after a new tab was added or
1344 inserted at position \a index.
1345
1346 \sa tabRemoved()
1347 */
1348void QTabBar::tabInserted(int index)
1349{
1350 Q_UNUSED(index)
1351}
1352
1353/*!
1354 This virtual handler is called after a tab was removed from
1355 position \a index.
1356
1357 \sa tabInserted()
1358 */
1359void QTabBar::tabRemoved(int index)
1360{
1361 Q_UNUSED(index)
1362}
1363
1364/*!
1365 This virtual handler is called whenever the tab layout changes.
1366
1367 \sa tabRect()
1368 */
1369void QTabBar::tabLayoutChange()
1370{
1371}
1372
1373
1374/*!\reimp
1375 */
1376void QTabBar::showEvent(QShowEvent *)
1377{
1378 Q_D(QTabBar);
1379 if (d->layoutDirty)
1380 d->refresh();
1381 if (!d->validIndex(d->currentIndex))
1382 setCurrentIndex(0);
1383 d->updateMacBorderMetrics();
1384}
1385
1386/*!\reimp
1387 */
1388void QTabBar::hideEvent(QHideEvent *)
1389{
1390 Q_D(QTabBar);
1391 d->updateMacBorderMetrics();
1392}
1393
1394/*!\reimp
1395 */
1396bool QTabBar::event(QEvent *event)
1397{
1398 Q_D(QTabBar);
1399 if (event->type() == QEvent::HoverMove
1400 || event->type() == QEvent::HoverEnter) {
1401 QHoverEvent *he = static_cast<QHoverEvent *>(event);
1402 if (!d->hoverRect.contains(he->pos())) {
1403 QRect oldHoverRect = d->hoverRect;
1404 for (int i = 0; i < d->tabList.count(); ++i) {
1405 QRect area = tabRect(i);
1406 if (area.contains(he->pos())) {
1407 d->hoverRect = area;
1408 break;
1409 }
1410 }
1411 if (he->oldPos() != QPoint(-1, -1))
1412 update(oldHoverRect);
1413 update(d->hoverRect);
1414 }
1415 return true;
1416 } else if (event->type() == QEvent::HoverLeave ) {
1417 QRect oldHoverRect = d->hoverRect;
1418 d->hoverRect = QRect();
1419 update(oldHoverRect);
1420 return true;
1421#ifndef QT_NO_TOOLTIP
1422 } else if (event->type() == QEvent::ToolTip) {
1423 if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
1424 if (!tab->toolTip.isEmpty()) {
1425 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
1426 return true;
1427 }
1428 }
1429#endif // QT_NO_TOOLTIP
1430#ifndef QT_NO_WHATSTHIS
1431 } else if (event->type() == QEvent::QueryWhatsThis) {
1432 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
1433 if (!tab || tab->whatsThis.isEmpty())
1434 event->ignore();
1435 return true;
1436 } else if (event->type() == QEvent::WhatsThis) {
1437 if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
1438 if (!tab->whatsThis.isEmpty()) {
1439 QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
1440 tab->whatsThis, this);
1441 return true;
1442 }
1443 }
1444#endif // QT_NO_WHATSTHIS
1445#ifndef QT_NO_SHORTCUT
1446 } else if (event->type() == QEvent::Shortcut) {
1447 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1448 for (int i = 0; i < d->tabList.count(); ++i) {
1449 const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
1450 if (tab->shortcutId == se->shortcutId()) {
1451 setCurrentIndex(i);
1452 return true;
1453 }
1454 }
1455#endif
1456 }
1457 return QWidget::event(event);
1458}
1459
1460/*!\reimp
1461 */
1462void QTabBar::resizeEvent(QResizeEvent *)
1463{
1464 Q_D(QTabBar);
1465 if (d->layoutDirty)
1466 updateGeometry();
1467 d->layoutTabs();
1468
1469 d->makeVisible(d->currentIndex);
1470}
1471
1472/*!\reimp
1473 */
1474void QTabBar::paintEvent(QPaintEvent *)
1475{
1476 Q_D(QTabBar);
1477
1478 QStyleOptionTabBarBaseV2 optTabBase;
1479 QTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());
1480
1481 QStylePainter p(this);
1482 int selected = -1;
1483 int cut = -1;
1484 bool rtl = optTabBase.direction == Qt::RightToLeft;
1485 bool vertical = verticalTabs(d->shape);
1486 QStyleOptionTab cutTab;
1487 selected = d->currentIndex;
1488 if (d->dragInProgress)
1489 selected = d->pressedIndex;
1490
1491 for (int i = 0; i < d->tabList.count(); ++i)
1492 optTabBase.tabBarRect |= tabRect(i);
1493
1494 optTabBase.selectedTabRect = tabRect(selected);
1495
1496 if (d->drawBase)
1497 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1498
1499 for (int i = 0; i < d->tabList.count(); ++i) {
1500 QStyleOptionTabV3 tab;
1501 initStyleOption(&tab, i);
1502 if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
1503 if (vertical) {
1504 tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
1505 } else {
1506 tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
1507 }
1508 }
1509 if (!(tab.state & QStyle::State_Enabled)) {
1510 tab.palette.setCurrentColorGroup(QPalette::Disabled);
1511 }
1512 // If this tab is partially obscured, make a note of it so that we can pass the information
1513 // along when we draw the tear.
1514 if (((!vertical && (!rtl && tab.rect.left() < 0)) || (rtl && tab.rect.right() > width()))
1515 || (vertical && tab.rect.top() < 0)) {
1516 cut = i;
1517 cutTab = tab;
1518 }
1519 // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
1520 if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
1521 || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
1522 continue;
1523
1524 optTabBase.tabBarRect |= tab.rect;
1525 if (i == selected)
1526 continue;
1527
1528 p.drawControl(QStyle::CE_TabBarTab, tab);
1529 }
1530
1531 // Draw the selected tab last to get it "on top"
1532 if (selected >= 0) {
1533 QStyleOptionTabV3 tab;
1534 initStyleOption(&tab, selected);
1535 if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
1536 if (vertical)
1537 tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
1538 else
1539 tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
1540 }
1541 if (!d->dragInProgress)
1542 p.drawControl(QStyle::CE_TabBarTab, tab);
1543 else {
1544 int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, this);
1545 d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
1546 }
1547 }
1548
1549 // Only draw the tear indicator if necessary. Most of the time we don't need too.
1550 if (d->leftB->isVisible() && cut >= 0) {
1551 cutTab.rect = rect();
1552 cutTab.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicator, &cutTab, this);
1553 p.drawPrimitive(QStyle::PE_IndicatorTabTear, cutTab);
1554 }
1555}
1556
1557/*
1558 Given that index at position from moved to position to where return where index goes.
1559 */
1560int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
1561{
1562 if (index == from)
1563 return to;
1564
1565 int start = qMin(from, to);
1566 int end = qMax(from, to);
1567 if (index >= start && index <= end)
1568 index += (from < to) ? -1 : 1;
1569 return index;
1570}
1571
1572/*!
1573 Moves the item at index position \a from to index position \a to.
1574 \since 4.5
1575
1576 \sa tabMoved(), tabLayoutChange()
1577 */
1578void QTabBar::moveTab(int from, int to)
1579{
1580 Q_D(QTabBar);
1581 if (from == to
1582 || !d->validIndex(from)
1583 || !d->validIndex(to))
1584 return;
1585
1586 bool vertical = verticalTabs(d->shape);
1587 int oldPressedPosition = 0;
1588 if (d->pressedIndex != -1) {
1589 // Record the position of the pressed tab before reordering the tabs.
1590 oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
1591 : d->tabList[d->pressedIndex].rect.x();
1592 }
1593
1594 // Update the locations of the tabs first
1595 int start = qMin(from, to);
1596 int end = qMax(from, to);
1597 int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
1598 if (from < to)
1599 width *= -1;
1600 bool rtl = isRightToLeft();
1601 for (int i = start; i <= end; ++i) {
1602 if (i == from)
1603 continue;
1604 if (vertical)
1605 d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
1606 else
1607 d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
1608 int direction = -1;
1609 if (rtl && !vertical)
1610 direction *= -1;
1611 if (d->tabList[i].dragOffset != 0)
1612 d->tabList[i].dragOffset += (direction * width);
1613 }
1614
1615 if (vertical) {
1616 if (from < to)
1617 d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
1618 else
1619 d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
1620 } else {
1621 if (from < to)
1622 d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
1623 else
1624 d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
1625 }
1626
1627 // Move the actual data structures
1628 d->tabList.move(from, to);
1629
1630 // update lastTab locations
1631 for (int i = 0; i < d->tabList.count(); ++i)
1632 d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
1633
1634 // update external variables
1635 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
1636
1637 // If we are in the middle of a drag update the dragStartPosition
1638 if (d->pressedIndex != -1) {
1639 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
1640 int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
1641 int diff = oldPressedPosition - newPressedPosition;
1642 if (isRightToLeft() && !vertical)
1643 diff *= -1;
1644 if (vertical)
1645 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
1646 else
1647 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
1648 }
1649
1650 d->layoutWidgets(start);
1651 update();
1652 emit tabMoved(from, to);
1653 emit tabLayoutChange();
1654}
1655
1656void QTabBarPrivate::slide(int from, int to)
1657{
1658 Q_Q(QTabBar);
1659 if (from == to
1660 || !validIndex(from)
1661 || !validIndex(to))
1662 return;
1663 bool vertical = verticalTabs(shape);
1664 int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
1665 q->setUpdatesEnabled(false);
1666 q->moveTab(from, to);
1667 q->setUpdatesEnabled(true);
1668 int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
1669 int length = postLocation - preLocation;
1670 tabList[to].dragOffset -= length;
1671 tabList[to].startAnimation(this, ANIMATION_DURATION);
1672}
1673
1674void QTabBarPrivate::moveTab(int index, int offset)
1675{
1676 if (!validIndex(index))
1677 return;
1678 tabList[index].dragOffset = offset;
1679 layoutTab(index); // Make buttons follow tab
1680 q_func()->update();
1681}
1682
1683/*!\reimp
1684*/
1685void QTabBar::mousePressEvent(QMouseEvent *event)
1686{
1687 Q_D(QTabBar);
1688 if (event->button() != Qt::LeftButton) {
1689 event->ignore();
1690 return;
1691 }
1692 // Be safe!
1693 if (d->pressedIndex != -1 && d->movable)
1694 d->moveTabFinished(d->pressedIndex);
1695
1696 d->pressedIndex = d->indexAtPos(event->pos());
1697#ifdef Q_WS_MAC
1698 d->previousPressedIndex = d->pressedIndex;
1699#endif
1700 if (d->validIndex(d->pressedIndex)) {
1701 QStyleOptionTabBarBaseV2 optTabBase;
1702 optTabBase.init(this);
1703 optTabBase.documentMode = d->documentMode;
1704 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
1705 setCurrentIndex(d->pressedIndex);
1706 else
1707 repaint(tabRect(d->pressedIndex));
1708 if (d->movable) {
1709 d->dragStartPosition = event->pos();
1710 }
1711 }
1712}
1713
1714/*!\reimp
1715 */
1716void QTabBar::mouseMoveEvent(QMouseEvent *event)
1717{
1718 Q_D(QTabBar);
1719 if (d->movable) {
1720 // Be safe!
1721 if (d->pressedIndex != -1
1722 && event->buttons() == Qt::NoButton)
1723 d->moveTabFinished(d->pressedIndex);
1724
1725 // Start drag
1726 if (!d->dragInProgress && d->pressedIndex != -1) {
1727 if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
1728 d->dragInProgress = true;
1729 d->setupMovableTab();
1730 }
1731 }
1732
1733 int offset = (event->pos() - d->dragStartPosition).manhattanLength();
1734 if (event->buttons() == Qt::LeftButton
1735 && offset > QApplication::startDragDistance()
1736 && d->validIndex(d->pressedIndex)) {
1737 bool vertical = verticalTabs(d->shape);
1738 int dragDistance;
1739 if (vertical) {
1740 dragDistance = (event->pos().y() - d->dragStartPosition.y());
1741 } else {
1742 dragDistance = (event->pos().x() - d->dragStartPosition.x());
1743 }
1744 d->tabList[d->pressedIndex].dragOffset = dragDistance;
1745
1746 QRect startingRect = tabRect(d->pressedIndex);
1747 if (vertical)
1748 startingRect.moveTop(startingRect.y() + dragDistance);
1749 else
1750 startingRect.moveLeft(startingRect.x() + dragDistance);
1751
1752 int overIndex;
1753 if (dragDistance < 0)
1754 overIndex = tabAt(startingRect.topLeft());
1755 else
1756 overIndex = tabAt(startingRect.topRight());
1757
1758 if (overIndex != d->pressedIndex && overIndex != -1) {
1759 int offset = 1;
1760 if (isRightToLeft() && !vertical)
1761 offset *= -1;
1762 if (dragDistance < 0) {
1763 dragDistance *= -1;
1764 offset *= -1;
1765 }
1766 for (int i = d->pressedIndex;
1767 offset > 0 ? i < overIndex : i > overIndex;
1768 i += offset) {
1769 QRect overIndexRect = tabRect(overIndex);
1770 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
1771 if (dragDistance > needsToBeOver)
1772 d->slide(i + offset, d->pressedIndex);
1773 }
1774 }
1775 // Buttons needs to follow the dragged tab
1776 d->layoutTab(d->pressedIndex);
1777
1778 update();
1779 }
1780#ifdef Q_WS_MAC
1781 } else if (!d->documentMode && event->buttons() == Qt::LeftButton && d->previousPressedIndex != -1) {
1782 int newPressedIndex = d->indexAtPos(event->pos());
1783 if (d->pressedIndex == -1 && d->previousPressedIndex == newPressedIndex) {
1784 d->pressedIndex = d->previousPressedIndex;
1785 update(tabRect(d->pressedIndex));
1786 } else if(d->pressedIndex != newPressedIndex) {
1787 d->pressedIndex = -1;
1788 update(tabRect(d->previousPressedIndex));
1789 }
1790#endif
1791 }
1792
1793 if (event->buttons() != Qt::LeftButton) {
1794 event->ignore();
1795 return;
1796 }
1797 QStyleOptionTabBarBaseV2 optTabBase;
1798 optTabBase.init(this);
1799 optTabBase.documentMode = d->documentMode;
1800}
1801
1802void QTabBarPrivate::setupMovableTab()
1803{
1804 Q_Q(QTabBar);
1805 if (!movingTab)
1806 movingTab = new QWidget(q);
1807
1808 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0 ,q);
1809 QRect grabRect = q->tabRect(pressedIndex);
1810 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
1811
1812 QPixmap grabImage(grabRect.size());
1813 grabImage.fill(Qt::transparent);
1814 QStylePainter p(&grabImage, q);
1815 p.initFrom(q);
1816
1817 QStyleOptionTabV3 tab;
1818 q->initStyleOption(&tab, pressedIndex);
1819 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
1820 p.drawControl(QStyle::CE_TabBarTab, tab);
1821 p.end();
1822
1823 QPalette pal;
1824 pal.setBrush(QPalette::All, QPalette::Window, grabImage);
1825 movingTab->setPalette(pal);
1826 movingTab->setGeometry(grabRect);
1827 movingTab->setAutoFillBackground(true);
1828 movingTab->raise();
1829
1830 // Re-arrange widget order to avoid overlaps
1831 if (tabList[pressedIndex].leftWidget)
1832 tabList[pressedIndex].leftWidget->raise();
1833 if (tabList[pressedIndex].rightWidget)
1834 tabList[pressedIndex].rightWidget->raise();
1835 if (leftB)
1836 leftB->raise();
1837 if (rightB)
1838 rightB->raise();
1839 movingTab->setVisible(true);
1840}
1841
1842void QTabBarPrivate::moveTabFinished(int index)
1843{
1844 Q_Q(QTabBar);
1845 bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
1846 bool allAnimationsFinished = true;
1847#ifndef QT_NO_ANIMATION
1848 for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
1849 const Tab &t = tabList.at(i);
1850 if (t.animation && t.animation->state() == QAbstractAnimation::Running)
1851 allAnimationsFinished = false;
1852 }
1853#endif //QT_NO_ANIMATION
1854 if (allAnimationsFinished && cleanup) {
1855 if(movingTab)
1856 movingTab->setVisible(false); // We might not get a mouse release
1857 for (int i = 0; i < tabList.count(); ++i) {
1858 tabList[i].dragOffset = 0;
1859 }
1860 if (pressedIndex != -1 && movable) {
1861 pressedIndex = -1;
1862 dragInProgress = false;
1863 dragStartPosition = QPoint();
1864 }
1865 layoutWidgets();
1866 } else {
1867 if (!validIndex(index))
1868 return;
1869 tabList[index].dragOffset = 0;
1870 }
1871 q->update();
1872}
1873
1874/*!\reimp
1875*/
1876void QTabBar::mouseReleaseEvent(QMouseEvent *event)
1877{
1878 Q_D(QTabBar);
1879 if (event->button() != Qt::LeftButton) {
1880 event->ignore();
1881 return;
1882 }
1883#ifdef Q_WS_MAC
1884 d->previousPressedIndex = -1;
1885#endif
1886 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
1887 int length = d->tabList[d->pressedIndex].dragOffset;
1888 int width = verticalTabs(d->shape)
1889 ? tabRect(d->pressedIndex).height()
1890 : tabRect(d->pressedIndex).width();
1891 int duration = qMin(ANIMATION_DURATION,
1892 (qAbs(length) * ANIMATION_DURATION) / width);
1893 d->tabList[d->pressedIndex].startAnimation(d, duration);
1894 d->dragInProgress = false;
1895 d->movingTab->setVisible(false);
1896 d->dragStartPosition = QPoint();
1897 }
1898
1899 int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
1900 d->pressedIndex = -1;
1901 QStyleOptionTabBarBaseV2 optTabBase;
1902 optTabBase.initFrom(this);
1903 optTabBase.documentMode = d->documentMode;
1904 if (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease)
1905 setCurrentIndex(i);
1906}
1907
1908/*!\reimp
1909 */
1910void QTabBar::keyPressEvent(QKeyEvent *event)
1911{
1912 Q_D(QTabBar);
1913 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
1914 event->ignore();
1915 return;
1916 }
1917 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
1918 d->setCurrentNextEnabledIndex(offset);
1919}
1920
1921/*!\reimp
1922 */
1923#ifndef QT_NO_WHEELEVENT
1924void QTabBar::wheelEvent(QWheelEvent *event)
1925{
1926 Q_D(QTabBar);
1927 int offset = event->delta() > 0 ? -1 : 1;
1928 d->setCurrentNextEnabledIndex(offset);
1929 QWidget::wheelEvent(event);
1930}
1931#endif //QT_NO_WHEELEVENT
1932
1933void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
1934{
1935 Q_Q(QTabBar);
1936 for (int index = currentIndex + offset; validIndex(index); index += offset) {
1937 if (tabList.at(index).enabled) {
1938 q->setCurrentIndex(index);
1939 break;
1940 }
1941 }
1942}
1943
1944/*!\reimp
1945 */
1946void QTabBar::changeEvent(QEvent *event)
1947{
1948 Q_D(QTabBar);
1949 if (event->type() == QEvent::StyleChange) {
1950 d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
1951 if (!d->useScrollButtonsSetByUser)
1952 d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
1953 d->refresh();
1954 } else if (event->type() == QEvent::FontChange) {
1955 d->refresh();
1956 }
1957 QWidget::changeEvent(event);
1958}
1959
1960/*!
1961 \property QTabBar::elideMode
1962 \brief how to elide text in the tab bar
1963 \since 4.2
1964
1965 This property controls how items are elided when there is not
1966 enough space to show them for a given tab bar size.
1967
1968 By default the value is style dependent.
1969
1970 \sa QTabWidget::elideMode usesScrollButtons QStyle::SH_TabBar_ElideMode
1971*/
1972
1973Qt::TextElideMode QTabBar::elideMode() const
1974{
1975 Q_D(const QTabBar);
1976 return d->elideMode;
1977}
1978
1979void QTabBar::setElideMode(Qt::TextElideMode mode)
1980{
1981 Q_D(QTabBar);
1982 d->elideMode = mode;
1983 d->refresh();
1984}
1985
1986/*!
1987 \property QTabBar::usesScrollButtons
1988 \brief Whether or not a tab bar should use buttons to scroll tabs when it
1989 has many tabs.
1990 \since 4.2
1991
1992 When there are too many tabs in a tab bar for its size, the tab bar can either choose
1993 to expand its size or to add buttons that allow you to scroll through the tabs.
1994
1995 By default the value is style dependant.
1996
1997 \sa elideMode QTabWidget::usesScrollButtons QStyle::SH_TabBar_PreferNoArrows
1998*/
1999bool QTabBar::usesScrollButtons() const
2000{
2001 return d_func()->useScrollButtons;
2002}
2003
2004void QTabBar::setUsesScrollButtons(bool useButtons)
2005{
2006 Q_D(QTabBar);
2007 d->useScrollButtonsSetByUser = true;
2008 if (d->useScrollButtons == useButtons)
2009 return;
2010 d->useScrollButtons = useButtons;
2011 d->refresh();
2012}
2013
2014/*!
2015 \fn void QTabBar::setCurrentTab(int index)
2016
2017 Use setCurrentIndex() instead.
2018*/
2019
2020/*!
2021 \fn void QTabBar::selected(int index);
2022
2023 Use currentChanged() instead.
2024*/
2025
2026
2027/*!
2028 \property QTabBar::tabsClosable
2029 \brief Whether or not a tab bar should place close buttons on each tab
2030 \since 4.5
2031
2032 When tabsClosable is set to true a close button will appear on the tab on
2033 either the left or right hand side depending upon the style. When the button
2034 is clicked the tab the signal tabCloseRequested will be emitted.
2035
2036 By default the value is false.
2037
2038 \sa setTabButton(), tabRemoved()
2039*/
2040
2041bool QTabBar::tabsClosable() const
2042{
2043 Q_D(const QTabBar);
2044 return d->closeButtonOnTabs;
2045}
2046
2047void QTabBar::setTabsClosable(bool closable)
2048{
2049 Q_D(QTabBar);
2050 if (d->closeButtonOnTabs == closable)
2051 return;
2052 d->closeButtonOnTabs = closable;
2053 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
2054 if (!closable) {
2055 for (int i = 0; i < d->tabList.count(); ++i) {
2056 if (closeSide == LeftSide && d->tabList[i].leftWidget) {
2057 d->tabList[i].leftWidget->deleteLater();
2058 d->tabList[i].leftWidget = 0;
2059 }
2060 if (closeSide == RightSide && d->tabList[i].rightWidget) {
2061 d->tabList[i].rightWidget->deleteLater();
2062 d->tabList[i].rightWidget = 0;
2063 }
2064 }
2065 } else {
2066 bool newButtons = false;
2067 for (int i = 0; i < d->tabList.count(); ++i) {
2068 if (tabButton(i, closeSide))
2069 continue;
2070 newButtons = true;
2071 QAbstractButton *closeButton = new CloseButton(this);
2072 connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
2073 setTabButton(i, closeSide, closeButton);
2074 }
2075 if (newButtons)
2076 d->layoutTabs();
2077 }
2078 update();
2079}
2080
2081/*!
2082 \enum QTabBar::ButtonPosition
2083 \since 4.5
2084
2085 This enum type lists the location of the widget on a tab.
2086
2087 \value LeftSide Left side of the tab.
2088
2089 \value RightSide Right side of the tab.
2090
2091*/
2092
2093/*!
2094 \enum QTabBar::SelectionBehavior
2095 \since 4.5
2096
2097 This enum type lists the behavior of QTabBar when a tab is removed
2098 and the tab being removed is also the current tab.
2099
2100 \value SelectLeftTab Select the tab to the left of the one being removed.
2101
2102 \value SelectRightTab Select the tab to the right of the one being removed.
2103
2104 \value SelectPreviousTab Select the previously selected tab.
2105
2106*/
2107
2108/*!
2109 \property QTabBar::selectionBehaviorOnRemove
2110 \brief What tab should be set as current when removeTab is called if
2111 the removed tab is also the current tab.
2112 \since 4.5
2113
2114 By default the value is SelectRightTab.
2115
2116 \sa removeTab()
2117*/
2118
2119
2120QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2121{
2122 Q_D(const QTabBar);
2123 return d->selectionBehaviorOnRemove;
2124}
2125
2126void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2127{
2128 Q_D(QTabBar);
2129 d->selectionBehaviorOnRemove = behavior;
2130}
2131
2132/*!
2133 \property QTabBar::expanding
2134 \brief When expanding is true QTabBar will expand the tabs to use the empty space.
2135 \since 4.5
2136
2137 By default the value is true.
2138
2139 \sa QTabWidget::documentMode
2140*/
2141
2142bool QTabBar::expanding() const
2143{
2144 Q_D(const QTabBar);
2145 return d->expanding;
2146}
2147
2148void QTabBar::setExpanding(bool enabled)
2149{
2150 Q_D(QTabBar);
2151 if (d->expanding == enabled)
2152 return;
2153 d->expanding = enabled;
2154 d->layoutTabs();
2155}
2156
2157/*!
2158 \property QTabBar::movable
2159 \brief This property holds whether the user can move the tabs
2160 within the tabbar area.
2161
2162 \since 4.5
2163
2164 By default, this property is false;
2165*/
2166
2167bool QTabBar::isMovable() const
2168{
2169 Q_D(const QTabBar);
2170 return d->movable;
2171}
2172
2173void QTabBar::setMovable(bool movable)
2174{
2175 Q_D(QTabBar);
2176 d->movable = movable;
2177}
2178
2179
2180/*!
2181 \property QTabBar::documentMode
2182 \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
2183 \since 4.5
2184
2185 This property is used as a hint for styles to draw the tabs in a different
2186 way then they would normally look in a tab widget. On Mac OS X this will
2187 look similar to the tabs in Safari or Leopard's Terminal.app.
2188
2189 \sa QTabWidget::documentMode
2190*/
2191bool QTabBar::documentMode() const
2192{
2193 return d_func()->documentMode;
2194}
2195
2196void QTabBar::setDocumentMode(bool enabled)
2197{
2198 Q_D(QTabBar);
2199 d->documentMode = enabled;
2200 d->updateMacBorderMetrics();
2201}
2202
2203/*!
2204 Sets \a widget on the tab \a index. The widget is placed
2205 on the left or right hand side depending upon the \a position.
2206 \since 4.5
2207
2208 Any previously set widget in \a position is hidden.
2209
2210 The tab bar will take ownership of the widget and so all widgets set here
2211 will be deleted by the tab bar when it is destroyed unless you separately
2212 reparent the widget after setting some other widget (or 0).
2213
2214 \sa tabsClosable()
2215 */
2216void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
2217{
2218 Q_D(QTabBar);
2219 if (index < 0 || index >= d->tabList.count())
2220 return;
2221 if (widget) {
2222 widget->setParent(this);
2223 // make sure our left and right widgets stay on top
2224 widget->lower();
2225 widget->show();
2226 }
2227 if (position == LeftSide) {
2228 if (d->tabList[index].leftWidget)
2229 d->tabList[index].leftWidget->hide();
2230 d->tabList[index].leftWidget = widget;
2231 } else {
2232 if (d->tabList[index].rightWidget)
2233 d->tabList[index].rightWidget->hide();
2234 d->tabList[index].rightWidget = widget;
2235 }
2236 d->layoutTabs();
2237 d->refresh();
2238 update();
2239}
2240
2241/*!
2242 Returns the widget set a tab \a index and \a position or 0 if
2243 one is not set.
2244 */
2245QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
2246{
2247 Q_D(const QTabBar);
2248 if (index < 0 || index >= d->tabList.count())
2249 return 0;
2250 if (position == LeftSide)
2251 return d->tabList.at(index).leftWidget;
2252 else
2253 return d->tabList.at(index).rightWidget;
2254}
2255
2256CloseButton::CloseButton(QWidget *parent)
2257 : QAbstractButton(parent)
2258{
2259 setFocusPolicy(Qt::NoFocus);
2260#ifndef QT_NO_CURSOR
2261 setCursor(Qt::ArrowCursor);
2262#endif
2263#ifndef QT_NO_TOOLTIP
2264 setToolTip(tr("Close Tab"));
2265#endif
2266 resize(sizeHint());
2267}
2268
2269QSize CloseButton::sizeHint() const
2270{
2271 ensurePolished();
2272 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
2273 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
2274 return QSize(width, height);
2275}
2276
2277void CloseButton::enterEvent(QEvent *event)
2278{
2279 if (isEnabled())
2280 update();
2281 QAbstractButton::enterEvent(event);
2282}
2283
2284void CloseButton::leaveEvent(QEvent *event)
2285{
2286 if (isEnabled())
2287 update();
2288 QAbstractButton::leaveEvent(event);
2289}
2290
2291void CloseButton::paintEvent(QPaintEvent *)
2292{
2293 QPainter p(this);
2294 QStyleOption opt;
2295 opt.init(this);
2296 opt.state |= QStyle::State_AutoRaise;
2297 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2298 opt.state |= QStyle::State_Raised;
2299 if (isChecked())
2300 opt.state |= QStyle::State_On;
2301 if (isDown())
2302 opt.state |= QStyle::State_Sunken;
2303
2304 if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
2305 int index = tb->currentIndex();
2306 QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
2307 if (tb->tabButton(index, position) == this)
2308 opt.state |= QStyle::State_Selected;
2309 }
2310
2311 style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
2312}
2313
2314QT_END_NAMESPACE
2315
2316#include "moc_qtabbar.cpp"
2317
2318#endif // QT_NO_TABBAR
Note: See TracBrowser for help on using the repository browser.