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