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