4#include "private/qlayoutengine_p.h"
5#if QT_CONFIG(itemviews)
6#include "qabstractitemdelegate.h"
14#if QT_CONFIG(tabwidget)
15#include "qtabwidget.h"
20#if QT_CONFIG(whatsthis)
21#include "qwhatsthis.h"
23#include "private/qtextengine_p.h"
24#if QT_CONFIG(accessibility)
25#include "qaccessible.h"
28#include <qpa/qplatformnativeinterface.h>
32#include "private/qapplication_p.h"
33#include "private/qtabbar_p.h"
37using namespace Qt::StringLiterals;
38using namespace std::chrono_literals;
41class CloseButton :
public QAbstractButton
46 explicit CloseButton(QWidget *parent =
nullptr);
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;
57QMovableTabWidget::QMovableTabWidget(QWidget *parent)
62void QMovableTabWidget::setPixmap(
const QPixmap &pixmap)
68void QMovableTabWidget::paintEvent(QPaintEvent *e)
72 p.drawPixmap(0, 0, m_pixmap);
75void QTabBarPrivate::updateMacBorderMetrics()
77#if defined(Q_OS_MACOS)
88 QPoint windowPos = q->mapTo(q->window(), QPoint(0,0));
89 upper = windowPos.y();
90 int tabStripHeight = q->tabSizeHint(0).height();
92 lower = upper + tabStripHeight + pixelTweak;
98 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
101 quintptr identifier =
reinterpret_cast<quintptr>(q);
104 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
105 nativeInterface->nativeResourceFunctionForIntegration(
"registerContentBorderArea");
108 typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier,
int upper,
int lower);
109 (
reinterpret_cast<RegisterContentBorderAreaFunction>(function))(q->window()->windowHandle(), identifier, upper, lower);
112 function = nativeInterface->nativeResourceFunctionForIntegration(
"setContentBorderAreaEnabled");
115 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier,
bool enable);
116 (
reinterpret_cast<SetContentBorderAreaEnabledFunction>(function))(q->window()->windowHandle(), identifier, q->isVisible());
121
122
123
124
126void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option,
int tabIndex)
const
129 const int totalTabs = tabList.size();
131 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
134 const QTabBarPrivate::Tab &tab = *tabList.at(tabIndex);
136 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
137 option->rect = q->tabRect(tabIndex);
138 const bool isCurrent = tabIndex == currentIndex;
140 if (tabIndex == pressedIndex)
141 option->state |= QStyle::State_Sunken;
143 option->state |= QStyle::State_Selected;
144 if (isCurrent && q->hasFocus())
145 option->state |= QStyle::State_HasFocus;
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;
155 if (tab.textColor.isValid())
156 option->palette.setColor(q->foregroundRole(), tab.textColor);
157 option->icon = tab.icon;
158 option->iconSize = q->iconSize();
160 option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
161 option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
162 option->documentMode = documentMode;
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;
169 option->selectedPosition = QStyleOptionTab::NotAdjacent;
171 const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
172 const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
173 if (paintBeginning) {
175 option->position = QStyleOptionTab::OnlyOneTab;
177 option->position = QStyleOptionTab::Beginning;
178 }
else if (paintEnd) {
179 option->position = QStyleOptionTab::End;
181 option->position = QStyleOptionTab::Middle;
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;
193 if (tab.measuringMinimum)
194 option->features |= QStyleOptionTab::MinimumSizeHint;
195 option->tabIndex = tabIndex;
199
200
201
202
203
204
205void QTabBar::initStyleOption(QStyleOptionTab *option,
int tabIndex)
const
208 d->initBasicStyleOption(option, tabIndex);
210 QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option,
this);
211 option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
212 Qt::TextShowMnemonic);
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
316
317
318
319
320
321
324
325
326
327
328
329
330
331
334
335
336
337
338
339
340
341
342
343
344
347
348
349
350
351
352
353
354
357
358
359
360
361
362
363
364
366void QTabBarPrivate::init()
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);
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);
381#ifdef QT_KEYPAD_NAVIGATION
382 if (QApplicationPrivate::keypadNavigationEnabled()) {
383 leftB->setFocusPolicy(Qt::NoFocus);
384 rightB->setFocusPolicy(Qt::NoFocus);
385 q->setFocusPolicy(Qt::NoFocus);
388 q->setFocusPolicy(Qt::TabFocus);
390#if QT_CONFIG(accessibility)
391 leftB->setAccessibleName(QTabBar::tr(
"Scroll Left"));
392 rightB->setAccessibleName(QTabBar::tr(
"Scroll Right"));
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);
399int QTabBarPrivate::indexAtPos(
const QPoint &p)
const
402 if (q->tabRect(currentIndex).contains(p))
404 for (
int i = 0; i < tabList.size(); ++i)
405 if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
410void QTabBarPrivate::layoutTabs()
414 QSize size = q->size();
417 bool vertTabs = verticalTabs(shape);
418 int tabChainIndex = 0;
421 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
422 QList<QLayoutStruct> tabChain(tabList.size() + 2);
426 tabChain[tabChainIndex].init();
427 tabChain[tabChainIndex].expansive = (!expanding)
428 && (tabAlignment != Qt::AlignLeft)
429 && (tabAlignment != Qt::AlignJustify);
430 tabChain[tabChainIndex].empty =
true;
445 for (
int i = 0; i < tabList.size(); ++i) {
446 const auto tab = tabList.at(i);
451 QSize sz = q->tabSizeHint(i);
452 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
454 maxHeight = qMax(maxHeight, sz.height());
455 sz = q->minimumTabSizeHint(i);
456 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
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;
465 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
470 available = size.width();
471 maxExtent = maxHeight;
476 for (
int i = 0; i < tabList.size(); ++i) {
477 auto tab = tabList.at(i);
482 QSize sz = q->tabSizeHint(i);
483 tab->maxRect = QRect(0, y, sz.width(), sz.height());
485 maxWidth = qMax(maxWidth, sz.width());
486 sz = q->minimumTabSizeHint(i);
487 tab->minRect = QRect(0, miny, sz.width(), 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;
496 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
501 available = size.height();
502 maxExtent = maxWidth;
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);
514 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
518 for (
int i = 0; i < tabList.size(); ++i) {
519 auto tab = tabList.at(i);
525 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
527 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
529 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
532 if (useScrollButtons && tabList.size() && last > available) {
533 const QRect scrollRect = normalizedScrollRect(0);
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);
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);
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);
565 leftB->setGeometry(scrollButtonLeftRect);
566 leftB->setEnabled(
false);
569 rightB->setGeometry(scrollButtonRightRect);
570 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
578 q->tabLayoutChange();
581QRect QTabBarPrivate::normalizedScrollRect(
int index)
591 if (leftB->isHidden())
592 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
595 q->initStyleOption(&opt, currentIndex);
596 opt.rect = q->rect();
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);
603 if (verticalTabs(shape)) {
604 int topEdge, bottomEdge;
605 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
606 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
608 if (leftButtonIsOnTop && rightButtonIsOnTop) {
609 topEdge = scrollButtonRightRect.bottom() + 1;
610 bottomEdge = q->height();
611 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
613 bottomEdge = scrollButtonLeftRect.top();
615 topEdge = scrollButtonLeftRect.bottom() + 1;
616 bottomEdge = scrollButtonRightRect.top();
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();
626 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
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);
635 int leftEdge, rightEdge;
636 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
637 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
639 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
640 leftEdge = scrollButtonRightRect.right() + 1;
641 rightEdge = q->width();
642 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
644 rightEdge = scrollButtonLeftRect.left();
646 leftEdge = scrollButtonLeftRect.right() + 1;
647 rightEdge = scrollButtonRightRect.left();
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();
657 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
661int QTabBarPrivate::hoveredTabIndex()
const
670void QTabBarPrivate::makeVisible(
int index)
673 if (!validIndex(index))
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);
685 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
686 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
688 if (available >= lastTabEnd) {
691 }
else if (tabStart < scrolledTabBarStart) {
693 scrollOffset = tabStart - scrollRect.left();
694 }
else if (tabEnd > scrolledTabBarEnd) {
696 scrollOffset = qMax(0, tabEnd - scrollRect.right());
697 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
699 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
702 leftB->setEnabled(scrollOffset > -scrollRect.left());
703 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
705 if (oldScrollOffset != scrollOffset) {
711void QTabBarPrivate::killSwitchTabTimer()
713 switchTabTimer.stop();
714 switchTabCurrentIndex = -1;
717void QTabBarPrivate::layoutTab(
int index)
720 Q_ASSERT(index >= 0);
722 const Tab *tab = tabList.at(index);
723 bool vertical = verticalTabs(shape);
724 if (!(tab->leftWidget || tab->rightWidget))
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) {
734 p.setY(p.y() + tab->dragOffset);
736 p.setX(p.x() + tab->dragOffset);
738 tab->leftWidget->move(p);
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) {
745 p.setY(p.y() + tab->dragOffset);
747 p.setX(p.x() + tab->dragOffset);
749 tab->rightWidget->move(p);
753void QTabBarPrivate::layoutWidgets(
int start)
756 for (
int i = start; i < q->count(); ++i) {
761void QTabBarPrivate::autoHideTabs()
766 q->setVisible(q->count() > 1);
769void QTabBarPrivate::closeTab()
772 QObject *object = q->sender();
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) {
782 if (tabList.at(i)->rightWidget == object) {
788 if (tabToClose != -1)
789 emit q->tabCloseRequested(tabToClose);
792void QTabBarPrivate::scrollTabs()
795 const QObject *sender = q->sender();
796 const bool horizontal = !verticalTabs(shape);
797 const QRect scrollRect = normalizedScrollRect().translated(scrollOffset, 0);
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()) {
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) {
822void QTabBarPrivate::refresh()
827 if (pressedIndex != -1
829 && mouseButtons == Qt::NoButton) {
830 moveTabFinished(pressedIndex);
831 if (!validIndex(pressedIndex))
835 if (!q->isVisible()) {
839 makeVisible(currentIndex);
846
847
848QTabBar::QTabBar(QWidget* parent)
849 :QWidget(*
new QTabBarPrivate, parent, { })
857
858
864
865
866
867
868
871QTabBar::Shape QTabBar::shape()
const
877void QTabBar::setShape(Shape shape)
880 if (d->shape == shape)
887
888
889
890
891
892
893
894
896void QTabBar::setDrawBase(
bool drawBase)
899 if (d->drawBase == drawBase)
901 d->drawBase = drawBase;
905bool QTabBar::drawBase()
const
912
913
914
915int QTabBar::addTab(
const QString &text)
917 return insertTab(-1, text);
921
922
923
924
925
926int QTabBar::addTab(
const QIcon& icon,
const QString &text)
928 return insertTab(-1, icon, text);
932
933
934
935
936int QTabBar::insertTab(
int index,
const QString &text)
938 return insertTab(index, QIcon(), text);
942
943
944
945
946
947
948
949
950
951
952
953int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
956 if (!d->validIndex(index)) {
957 index = d->tabList.size();
958 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
960 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
962#ifndef QT_NO_SHORTCUT
963 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
965 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
967 if (d->tabList.size() == 1)
968 setCurrentIndex(index);
969 else if (index <= d->currentIndex)
972 if (index <= d->lastVisible)
975 d->lastVisible = index;
977 if (d->closeButtonOnTabs) {
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);
987 for (
const auto tab : std::as_const(d->tabList)) {
988 if (tab->lastTab >= index)
992 if (tabAt(d->mousePosition) == index) {
993 d->hoverIndex = index;
994 d->hoverRect = tabRect(index);
1004
1005
1006
1007
1008void QTabBar::removeTab(
int index)
1011 if (d->validIndex(index)) {
1012 auto removedTab = d->tabList.at(index);
1013 if (d->dragInProgress)
1014 d->moveTabFinished(d->pressedIndex);
1016#ifndef QT_NO_SHORTCUT
1017 releaseShortcut(d->tabList.at(index)->shortcutId);
1019 if (removedTab->leftWidget) {
1020 removedTab->leftWidget->hide();
1021 removedTab->leftWidget->deleteLater();
1022 removedTab->leftWidget =
nullptr;
1024 if (removedTab->rightWidget) {
1025 removedTab->rightWidget->hide();
1026 removedTab->rightWidget->deleteLater();
1027 removedTab->rightWidget =
nullptr;
1030 int newIndex = removedTab->lastTab;
1031 d->tabList.removeAt(index);
1033 for (
auto tab : std::as_const(d->tabList)) {
1034 if (tab->lastTab == index)
1036 if (tab->lastTab > index)
1040 d->calculateFirstLastVisible(index,
false,
true);
1042 if (index == d->currentIndex) {
1046 d->currentIndex = -1;
1047 if (d->tabList.size() > 0) {
1048 switch(d->selectionBehaviorOnRemove) {
1049 case SelectPreviousTab:
1050 if (newIndex > index)
1052 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1055 case SelectRightTab:
1056 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1059 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1065 if (d->validIndex(newIndex)) {
1067 int bump = d->tabList.at(newIndex)->lastTab;
1068 setCurrentIndex(newIndex);
1069 d->tabList.at(newIndex)->lastTab = bump;
1072 emit currentChanged(-1);
1075 emit currentChanged(-1);
1077 }
else if (index < d->currentIndex) {
1078 setCurrentIndex(d->currentIndex - 1);
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);
1089 d->hoverRect = QRect();
1098
1099
1100
1101bool QTabBar::isTabEnabled(
int index)
const
1104 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1105 return tab->enabled;
1110
1111
1112
1113void QTabBar::setTabEnabled(
int index,
bool enabled)
1116 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1117 tab->enabled = enabled;
1118#ifndef QT_NO_SHORTCUT
1119 setShortcutEnabled(tab->shortcutId, enabled);
1122 if (!enabled && index == d->currentIndex)
1123 setCurrentIndex(d->selectNewCurrentIndexFrom(index+1));
1124 else if (enabled && !isTabVisible(d->currentIndex))
1125 setCurrentIndex(d->selectNewCurrentIndexFrom(index));
1131
1132
1133
1134
1135bool QTabBar::isTabVisible(
int index)
const
1138 if (d->validIndex(index))
1139 return d->tabList.at(index)->visible;
1144
1145
1146
1147
1148void QTabBar::setTabVisible(
int index,
bool visible)
1151 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1152 d->layoutDirty = (visible != tab->visible);
1153 if (!d->layoutDirty)
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);
1163 d->calculateFirstLastVisible(index, visible,
false);
1164 if (!visible && index == d->currentIndex) {
1165 const int newindex = d->selectNewCurrentIndexFrom(index+1);
1166 setCurrentIndex(newindex);
1174
1175
1176
1177QString QTabBar::tabText(
int index)
const
1180 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1186
1187
1188void QTabBar::setTabText(
int index,
const QString &text)
1191 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1192 d->textSizes.remove(tab->text);
1194#ifndef QT_NO_SHORTCUT
1195 releaseShortcut(tab->shortcutId);
1196 tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1197 setShortcutEnabled(tab->shortcutId, tab->enabled);
1204
1205
1206
1207
1208
1209QColor QTabBar::tabTextColor(
int index)
const
1212 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1213 return tab->textColor;
1218
1219
1220
1221
1222
1223
1224void QTabBar::setTabTextColor(
int index,
const QColor &color)
1227 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1228 tab->textColor = color;
1229 update(tabRect(index));
1234
1235
1236
1237QIcon QTabBar::tabIcon(
int index)
const
1240 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1246
1247
1248void QTabBar::setTabIcon(
int index,
const QIcon & icon)
1251 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1252 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1254 if (simpleIconChange)
1255 update(tabRect(index));
1261#if QT_CONFIG(tooltip)
1263
1264
1265void QTabBar::setTabToolTip(
int index,
const QString & tip)
1268 if (QTabBarPrivate::Tab *tab = d->at(index))
1273
1274
1275
1276QString QTabBar::tabToolTip(
int index)
const
1279 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1280 return tab->toolTip;
1285#if QT_CONFIG(whatsthis)
1287
1288
1289
1290
1291
1292void QTabBar::setTabWhatsThis(
int index,
const QString &text)
1295 if (QTabBarPrivate::Tab *tab = d->at(index))
1296 tab->whatsThis = text;
1300
1301
1302
1303
1304
1305QString QTabBar::tabWhatsThis(
int index)
const
1308 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1309 return tab->whatsThis;
1316
1317
1318void QTabBar::setTabData(
int index,
const QVariant & data)
1321 if (QTabBarPrivate::Tab *tab = d->at(index))
1326
1327
1328
1329QVariant QTabBar::tabData(
int index)
const
1332 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1338
1339
1340
1341QRect QTabBar::tabRect(
int index)
const
1344 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1346 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1349 QRect r = tab->rect;
1350 if (verticalTabs(d->shape))
1351 r.translate(0, -d->scrollOffset);
1353 r.translate(-d->scrollOffset, 0);
1354 if (!verticalTabs(d->shape))
1355 r = QStyle::visualRect(layoutDirection(), rect(), r);
1362
1363
1364
1365
1367int QTabBar::tabAt(
const QPoint &position)
const
1370 if (d->validIndex(d->currentIndex)
1371 && tabRect(d->currentIndex).contains(position)) {
1372 return d->currentIndex;
1374 const int max = d->tabList.size();
1375 for (
int i = 0; i < max; ++i) {
1376 if (tabRect(i).contains(position)) {
1384
1385
1386
1387
1388
1390int QTabBar::currentIndex()
const
1393 if (d->validIndex(d->currentIndex))
1394 return d->currentIndex;
1399void QTabBar::setCurrentIndex(
int index)
1402 if (d->dragInProgress && d->pressedIndex != -1)
1404 if (d->currentIndex == index)
1407 int oldIndex = d->currentIndex;
1408 if (
auto tab = d->at(index)) {
1409 d->currentIndex = index;
1415 if (tabRect(index).size() != tabSizeHint(index))
1419 d->layoutDirty =
true;
1421 d->makeVisible(index);
1422 if (d->validIndex(oldIndex)) {
1423 tab->lastTab = oldIndex;
1424 d->layoutTab(oldIndex);
1426 d->layoutTab(index);
1427#if QT_CONFIG(accessibility)
1428 if (QAccessible::isActive()) {
1430 QAccessibleEvent focusEvent(
this, QAccessible::Focus);
1431 focusEvent.setChild(index);
1432 QAccessible::updateAccessibility(&focusEvent);
1434 QAccessibleEvent selectionEvent(
this, QAccessible::Selection);
1435 selectionEvent.setChild(index);
1436 QAccessible::updateAccessibility(&selectionEvent);
1439 emit currentChanged(index);
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453QSize QTabBar::iconSize()
const
1456 if (d->iconSize.isValid())
1458 int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize,
nullptr,
this);
1459 return QSize(iconExtent, iconExtent);
1463void QTabBar::setIconSize(
const QSize &size)
1467 d->layoutDirty =
true;
1473
1474
1475
1477int QTabBar::count()
const
1480 return d->tabList.size();
1485
1486QSize QTabBar::sizeHint()
const
1490 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1492 for (
const auto tab : d->tabList) {
1494 r = r.united(tab->maxRect);
1500
1501QSize QTabBar::minimumSizeHint()
const
1505 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1506 if (!d->useScrollButtons) {
1508 for (
const auto tab : d->tabList) {
1510 r = r.united(tab->minRect);
1514 if (verticalTabs(d->shape))
1515 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1517 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1523 if (text.size() <= 3)
1526 static const auto Ellipses =
"..."_L1;
1529 case Qt::ElideRight:
1530 ret = QStringView{text}.left(2) + Ellipses;
1532 case Qt::ElideMiddle:
1533 ret = QStringView{text}.left(1) + Ellipses + QStringView{text}.right(1);
1536 ret = Ellipses + QStringView{text}.right(2);
1546
1547
1548
1550QSize QTabBar::minimumTabSizeHint(
int index)
const
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;
1564
1565
1566QSize QTabBar::tabSizeHint(
int index)
const
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();
1579 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1580 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1582 int widgetWidth = 0;
1583 int widgetHeight = 0;
1585 if (!opt.leftButtonSize.isEmpty()) {
1587 widgetWidth += opt.leftButtonSize.width();
1588 widgetHeight += opt.leftButtonSize.height();
1590 if (!opt.rightButtonSize.isEmpty()) {
1592 widgetWidth += opt.rightButtonSize.width();
1593 widgetHeight += opt.rightButtonSize.height();
1595 if (!opt.icon.isNull())
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();
1603 if (verticalTabs(d->shape)) {
1604 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1605 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1607 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1608 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1611 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1618
1619
1620
1621
1622
1623void QTabBar::tabInserted(
int index)
1629
1630
1631
1632
1633
1634void QTabBar::tabRemoved(
int index)
1640
1641
1642
1643
1644void QTabBar::tabLayoutChange()
1650
1651void QTabBar::showEvent(QShowEvent *)
1656 if (!d->validIndex(d->currentIndex))
1659 d->makeVisible(d->currentIndex);
1660 d->updateMacBorderMetrics();
1664
1665void QTabBar::hideEvent(QHideEvent *)
1668 d->updateMacBorderMetrics();
1672
1673bool QTabBar::event(QEvent *event)
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);
1689 d->hoverRect = QRect();
1694 case QEvent::HoverLeave: {
1695 d->mousePosition = {-1, -1};
1696 if (d->hoverRect.isValid())
1697 update(d->hoverRect);
1699 d->hoverRect = QRect();
1700#if QT_CONFIG(wheelevent)
1701 d->accumulatedAngleDelta = QPoint();
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);
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())
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);
1732#ifndef QT_NO_SHORTCUT
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()) {
1747 d->updateMacBorderMetrics();
1749#if QT_CONFIG(draganddrop)
1751 case QEvent::DragEnter:
1752 if (d->changeCurrentOnDrag)
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);
1766 case QEvent::DragLeave:
1768 d->killSwitchTabTimer();
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();
1782 return QWidget::event(event);
1786
1787void QTabBar::resizeEvent(QResizeEvent *)
1796 d->makeVisible(d->currentIndex);
1800
1801void QTabBar::paintEvent(QPaintEvent *)
1805 QStyleOptionTabBarBase optTabBase;
1806 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1808 QStylePainter p(
this);
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();
1820 for (
int i = 0; i < d->tabList.size(); ++i)
1821 optTabBase.tabBarRect |= tabRect(i);
1823 optTabBase.selectedTabRect = tabRect(selected);
1826 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1830 if (d->leftB->isVisible() || d->rightB->isVisible()) {
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);
1842 for (
int i = 0; i < d->tabList.size(); ++i) {
1843 const auto tab = d->tabList.at(i);
1846 QStyleOptionTab tabOption;
1847 initStyleOption(&tabOption, i);
1848 if (d->paintWithOffsets && tab->dragOffset != 0) {
1850 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1852 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1855 if (!(tabOption.state & QStyle::State_Enabled)) {
1856 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
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) {
1866 cutTabLeft = tabOption;
1867 }
else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1869 cutTabRight = tabOption;
1873 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1874 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1877 optTabBase.tabBarRect |= tabOption.rect;
1881 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1885 if (selected >= 0) {
1886 QStyleOptionTab tabOption;
1887 const auto tab = d->tabList.at(selected);
1888 initStyleOption(&tabOption, selected);
1890 if (d->paintWithOffsets && tab->dragOffset != 0) {
1893 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1896 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1898 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
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);
1909 if (d->dragInProgress)
1910 d->movingTab->setGeometry(movingRect);
1912 p.drawControl(QStyle::CE_TabBarTab, tabOption);
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);
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);
1930
1931
1932
1933void QTabBarPrivate::calculateFirstLastVisible(
int index,
bool visible,
bool remove)
1936 firstVisible = qMin(index, firstVisible);
1937 lastVisible = qMax(index, lastVisible);
1939 if (remove || (index == firstVisible)) {
1941 for (
int i = 0; i < tabList.size(); ++i) {
1942 if (tabList.at(i)->visible) {
1948 if (remove || (index == lastVisible)) {
1950 for (
int i = tabList.size() - 1; i >= 0; --i) {
1951 if (tabList.at(i)->visible) {
1961
1962
1963
1964
1965int QTabBarPrivate::selectNewCurrentIndexFrom(
int fromIndex)
1968 for (
int i = fromIndex; i < tabList.size(); ++i) {
1969 if (at(i)->visible && at(i)->enabled) {
1975 for (
int i = fromIndex-1; i > -1; --i) {
1976 if (at(i)->visible && at(i)->enabled) {
1987
1988
1989int QTabBarPrivate::calculateNewPosition(
int from,
int to,
int index)
const
1994 int start = qMin(from, to);
1995 int end = qMax(from, to);
1996 if (index >= start && index <= end)
1997 index += (from < to) ? -1 : 1;
2002
2003
2004
2005
2006
2007void QTabBar::moveTab(
int from,
int to)
2011 || !d->validIndex(from)
2012 || !d->validIndex(to))
2015 auto &fromTab = *d->tabList.at(from);
2016 auto &toTab = *d->tabList.at(to);
2018 bool vertical = verticalTabs(d->shape);
2019 int oldPressedPosition = 0;
2020 if (d->pressedIndex != -1) {
2022 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2023 : d->tabList.at(d->pressedIndex)->rect.x();
2027 int start = qMin(from, to);
2028 int end = qMax(from, to);
2029 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2032 bool rtl = isRightToLeft();
2033 for (
int i = start; i <= end; ++i) {
2036 auto &tab = *d->tabList.at(i);
2038 tab.rect.moveTop(tab.rect.y() + width);
2040 tab.rect.moveLeft(tab.rect.x() + width);
2042 if (rtl && !vertical)
2044 if (tab.dragOffset != 0)
2045 tab.dragOffset += (direction * width);
2050 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2052 fromTab.rect.moveTop(toTab.rect.top() - width);
2055 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2057 fromTab.rect.moveLeft(toTab.rect.left() - width);
2061 d->tabList.move(from, to);
2064 for (
const auto tab : std::as_const(d->tabList))
2065 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2068 int previousIndex = d->currentIndex;
2069 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
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)
2080 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2082 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2085 d->layoutWidgets(start);
2087 emit tabMoved(from, to);
2088 if (previousIndex != d->currentIndex)
2089 emit currentChanged(d->currentIndex);
2090 emit tabLayoutChange();
2093void QTabBarPrivate::slide(
int from,
int to)
2097 || !validIndex(from)
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);
2111void QTabBarPrivate::moveTab(
int index,
int offset)
2113 if (!validIndex(index))
2115 tabList.at(index)->dragOffset = offset;
2121
2122void QTabBar::mousePressEvent(QMouseEvent *event)
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);
2134 if (event->button() != Qt::LeftButton) {
2139 if (d->pressedIndex != -1 && d->movable)
2140 d->moveTabFinished(d->pressedIndex);
2142 d->pressedIndex = d->indexAtPos(event->position().toPoint());
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);
2151 repaint(tabRect(d->pressedIndex));
2153 d->dragStartPosition = event->position().toPoint();
2159
2160void QTabBar::mouseMoveEvent(QMouseEvent *event)
2165 if (d->pressedIndex != -1
2166 && event->buttons() == Qt::NoButton)
2167 d->moveTabFinished(d->pressedIndex);
2170 if (!d->dragInProgress && d->pressedIndex != -1) {
2171 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2172 d->dragInProgress =
true;
2173 d->setupMovableTab();
2177 if (event->buttons() == Qt::LeftButton
2178 && d->dragInProgress
2179 && d->validIndex(d->pressedIndex)) {
2180 bool vertical = verticalTabs(d->shape);
2183 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2185 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2187 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2189 QRect startingRect = tabRect(d->pressedIndex);
2191 startingRect.moveTop(startingRect.y() + dragDistance);
2193 startingRect.moveLeft(startingRect.x() + dragDistance);
2196 if (dragDistance < 0)
2197 overIndex = tabAt(startingRect.topLeft());
2199 overIndex = tabAt(startingRect.topRight());
2201 if (overIndex != d->pressedIndex && overIndex != -1) {
2203 if (isRightToLeft() && !vertical)
2205 if (dragDistance < 0) {
2209 for (
int i = d->pressedIndex;
2210 offset > 0 ? i < overIndex : i > overIndex;
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);
2219 if (d->pressedIndex != -1)
2220 d->layoutTab(d->pressedIndex);
2226 if (event->buttons() != Qt::LeftButton) {
2232void QTabBarPrivate::setupMovableTab()
2236 movingTab =
new QMovableTabWidget(q);
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);
2243 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2245 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2246 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2247 grabImage.fill(Qt::transparent);
2248 QStylePainter p(&grabImage, q);
2250 QStyleOptionTab tab;
2251 q->initStyleOption(&tab, pressedIndex);
2252 tab.position = QStyleOptionTab::Moving;
2253 if (verticalTabs(shape))
2254 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2256 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2257 p.drawControl(QStyle::CE_TabBarTab, tab);
2260 movingTab->setPixmap(grabImage);
2261 movingTab->setGeometry(grabRect);
2265 const auto &pressedTab = *tabList.at(pressedIndex);
2266 if (pressedTab.leftWidget)
2267 pressedTab.leftWidget->raise();
2268 if (pressedTab.rightWidget)
2269 pressedTab.rightWidget->raise();
2274 movingTab->setVisible(
true);
2277void QTabBarPrivate::moveTabFinished(
int index)
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;
2290 if (allAnimationsFinished && cleanup) {
2292 movingTab->setVisible(
false);
2293 for (
auto tab : std::as_const(tabList)) {
2294 tab->dragOffset = 0;
2296 if (pressedIndex != -1 && movable) {
2298 dragInProgress =
false;
2299 dragStartPosition = QPoint();
2303 if (!validIndex(index))
2305 tabList.at(index)->dragOffset = 0;
2311
2312void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2315 if (event->button() != Qt::LeftButton) {
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();
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)
2344 if (d->validIndex(oldPressedIndex))
2345 update(tabRect(oldPressedIndex));
2349
2350void QTabBar::mouseDoubleClickEvent(QMouseEvent *event)
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));
2359 mousePressEvent(event);
2363
2364void QTabBar::keyPressEvent(QKeyEvent *event)
2367 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
2371 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
2372 d->setCurrentNextEnabledIndex(offset);
2376
2377#if QT_CONFIG(wheelevent)
2378void QTabBar::wheelEvent(QWheelEvent *event)
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)) {
2388 if (tabsVertical == wheelVertical)
2389 delta = wheelVertical ? event->pixelDelta().y() : event->pixelDelta().x();
2390 if (layoutDirection() == Qt::RightToLeft)
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();
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) {
2415 d->accumulatedAngleDelta += event->angleDelta();
2416 const int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
2417 const int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
2419 if (xSteps > 0 || ySteps > 0) {
2421 d->accumulatedAngleDelta = QPoint();
2422 }
else if (xSteps < 0 || ySteps < 0) {
2424 d->accumulatedAngleDelta = QPoint();
2426 const int oldCurrentIndex = d->currentIndex;
2427 d->setCurrentNextEnabledIndex(offset);
2428 if (oldCurrentIndex != d->currentIndex) {
2433 QWidget::wheelEvent(event);
2438void QTabBarPrivate::setCurrentNextEnabledIndex(
int offset)
2441 for (
int index = currentIndex + offset; validIndex(index); index += offset) {
2442 if (tabList.at(index)->enabled && tabList.at(index)->visible) {
2443 q->setCurrentIndex(index);
2450
2451void QTabBar::changeEvent(QEvent *event)
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);
2461 case QEvent::FontChange:
2462 d->textSizes.clear();
2469 QWidget::changeEvent(event);
2473
2474
2475void QTabBar::timerEvent(QTimerEvent *event)
2478 if (event->id() == d->switchTabTimer.id()) {
2479 d->switchTabTimer.stop();
2480 setCurrentIndex(d->switchTabCurrentIndex);
2481 d->switchTabCurrentIndex = -1;
2483 QWidget::timerEvent(event);
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2499Qt::TextElideMode QTabBar::elideMode()
const
2502 return d->elideMode;
2505void QTabBar::setElideMode(Qt::TextElideMode mode)
2508 d->elideMode = mode;
2509 d->elideModeSetByUser =
true;
2510 d->textSizes.clear();
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527bool QTabBar::usesScrollButtons()
const
2529 return d_func()->useScrollButtons;
2532void QTabBar::setUsesScrollButtons(
bool useButtons)
2535 d->useScrollButtonsSetByUser =
true;
2536 if (d->useScrollButtons == useButtons)
2538 d->useScrollButtons = useButtons;
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2556bool QTabBar::tabsClosable()
const
2559 return d->closeButtonOnTabs;
2562void QTabBar::setTabsClosable(
bool closable)
2565 if (d->closeButtonOnTabs == closable)
2567 d->closeButtonOnTabs = closable;
2568 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
2570 for (
auto tab : std::as_const(d->tabList)) {
2571 if (closeSide == LeftSide && tab->leftWidget) {
2572 tab->leftWidget->deleteLater();
2573 tab->leftWidget =
nullptr;
2575 if (closeSide == RightSide && tab->rightWidget) {
2576 tab->rightWidget->deleteLater();
2577 tab->rightWidget =
nullptr;
2581 bool newButtons =
false;
2582 for (
int i = 0; i < d->tabList.size(); ++i) {
2583 if (tabButton(i, closeSide))
2586 QAbstractButton *closeButton =
new CloseButton(
this);
2587 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
2588 d, &QTabBarPrivate::closeTab);
2589 setTabButton(i, closeSide, closeButton);
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2625
2626
2627
2628
2629
2630
2631
2632
2633
2636QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove()
const
2639 return d->selectionBehaviorOnRemove;
2642void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2645 d->selectionBehaviorOnRemove = behavior;
2649
2650
2651
2652
2653
2654
2655
2656
2658bool QTabBar::expanding()
const
2661 return d->expanding;
2664void QTabBar::setExpanding(
bool enabled)
2667 if (d->expanding == enabled)
2669 d->expanding = enabled;
2674
2675
2676
2677
2678
2679
2680
2681
2683bool QTabBar::isMovable()
const
2689void QTabBar::setMovable(
bool movable)
2692 d->movable = movable;
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707bool QTabBar::documentMode()
const
2709 return d_func()->documentMode;
2712void QTabBar::setDocumentMode(
bool enabled)
2716 d->documentMode = enabled;
2717 d->updateMacBorderMetrics();
2721
2722
2723
2724
2725
2726
2727
2728
2729
2731bool QTabBar::autoHide()
const
2737void QTabBar::setAutoHide(
bool hide)
2740 if (d->autoHide == hide)
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2762bool QTabBar::changeCurrentOnDrag()
const
2765 return d->changeCurrentOnDrag;
2768void QTabBar::setChangeCurrentOnDrag(
bool change)
2771 d->changeCurrentOnDrag = change;
2773 d->killSwitchTabTimer();
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790void QTabBar::setTabButton(
int index, ButtonPosition position, QWidget *widget)
2793 if (index < 0 || index >= d->tabList.size())
2796 widget->setParent(
this);
2801 auto &tab = *d->tabList.at(index);
2802 if (position == LeftSide) {
2804 tab.leftWidget->hide();
2805 tab.leftWidget = widget;
2807 if (tab.rightWidget)
2808 tab.rightWidget->hide();
2809 tab.rightWidget = widget;
2817
2818
2819
2820QWidget *QTabBar::tabButton(
int index, ButtonPosition position)
const
2823 if (
const auto tab = d->at(index)) {
2824 return position == LeftSide ? tab->leftWidget
2830#if QT_CONFIG(accessibility)
2832
2833
2834void QTabBar::setAccessibleTabName(
int index,
const QString &name)
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);
2846
2847
2848
2849QString QTabBar::accessibleTabName(
int index)
const
2852 if (
const QTabBarPrivate::Tab *tab = d->at(index))
2853 return tab->accessibleName;
2858CloseButton::CloseButton(
QWidget *parent)
2859 : QAbstractButton(parent)
2861 setFocusPolicy(Qt::NoFocus);
2863 setCursor(Qt::ArrowCursor);
2865#if QT_CONFIG(tooltip)
2866 setToolTip(tr(
"Close Tab"));
2871QSize CloseButton::sizeHint()
const
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);
2879void CloseButton::enterEvent(QEnterEvent *event)
2883 QAbstractButton::enterEvent(event);
2886void CloseButton::leaveEvent(QEvent *event)
2890 QAbstractButton::leaveEvent(event);
2893void CloseButton::paintEvent(QPaintEvent *)
2898 opt.state |= QStyle::State_AutoRaise;
2899 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2900 opt.state |= QStyle::State_Raised;
2902 opt.state |= QStyle::State_On;
2904 opt.state |= QStyle::State_Sunken;
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;
2913 style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p,
this);
2916#if QT_CONFIG(animation)
2917void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(
const QVariant ¤t)
2919 priv->moveTab(priv->tabList.indexOf(tab), current.toInt());
2922void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
2924 if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(tab));
2930#include "moc_qtabbar.cpp"
2931#include "qtabbar.moc"
Combined button and popup list for selecting options.
static QString computeElidedText(Qt::TextElideMode mode, const QString &text)