5#include "private/qlayoutengine_p.h"
6#if QT_CONFIG(itemviews)
7#include "qabstractitemdelegate.h"
15#if QT_CONFIG(tabwidget)
16#include "qtabwidget.h"
21#if QT_CONFIG(whatsthis)
22#include "qwhatsthis.h"
24#include "private/qtextengine_p.h"
25#if QT_CONFIG(accessibility)
26#include "qaccessible.h"
29#include <qpa/qplatformnativeinterface.h>
33#include "private/qapplication_p.h"
34#include "private/qtabbar_p.h"
38using namespace Qt::StringLiterals;
39using namespace std::chrono_literals;
42class CloseButton :
public QAbstractButton
47 explicit CloseButton(QWidget *parent =
nullptr);
49 QSize sizeHint()
const override;
50 QSize minimumSizeHint()
const override
51 {
return sizeHint(); }
52 void enterEvent(QEnterEvent *event)
override;
53 void leaveEvent(QEvent *event)
override;
54 void paintEvent(QPaintEvent *event)
override;
58QMovableTabWidget::QMovableTabWidget(QWidget *parent)
63void QMovableTabWidget::setPixmap(
const QPixmap &pixmap)
69void QMovableTabWidget::paintEvent(QPaintEvent *e)
73 p.drawPixmap(0, 0, m_pixmap);
76void QTabBarPrivate::updateMacBorderMetrics()
78#if defined(Q_OS_MACOS)
89 QPoint windowPos = q->mapTo(q->window(), QPoint(0,0));
90 upper = windowPos.y();
91 int tabStripHeight = q->tabSizeHint(0).height();
93 lower = upper + tabStripHeight + pixelTweak;
99 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
100 if (!nativeInterface)
102 quintptr identifier =
reinterpret_cast<quintptr>(q);
105 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
106 nativeInterface->nativeResourceFunctionForIntegration(
"registerContentBorderArea");
109 typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier,
int upper,
int lower);
110 (
reinterpret_cast<RegisterContentBorderAreaFunction>(QFunctionPointer(function)))(
111 q->window()->windowHandle(), identifier, upper, lower);
114 function = nativeInterface->nativeResourceFunctionForIntegration(
"setContentBorderAreaEnabled");
117 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier,
bool enable);
118 (
reinterpret_cast<SetContentBorderAreaEnabledFunction>(QFunctionPointer(function)))(
119 q->window()->windowHandle(), identifier, q->isVisible());
124
125
126
127
129void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option,
int tabIndex)
const
132 const int totalTabs = tabList.size();
134 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
137 const QTabBarPrivate::Tab &tab = *tabList.at(tabIndex);
139 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
140 option->rect = q->tabRect(tabIndex);
141 const bool isCurrent = tabIndex == currentIndex;
143 if (tabIndex == pressedIndex)
144 option->state |= QStyle::State_Sunken;
146 option->state |= QStyle::State_Selected;
147 if (isCurrent && q->hasFocus())
148 option->state |= QStyle::State_HasFocus;
150 option->state &= ~QStyle::State_Enabled;
151 if (q->isActiveWindow())
152 option->state |= QStyle::State_Active;
153 if (!dragInProgress && option->rect == hoverRect)
154 option->state |= QStyle::State_MouseOver;
155 option->shape = shape;
156 option->text = tab.text;
158 if (tab.textColor.isValid())
159 option->palette.setColor(q->foregroundRole(), tab.textColor);
160 option->icon = tab.icon;
161 option->iconSize = q->iconSize();
163 option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
164 option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
165 option->documentMode = documentMode;
167 if (tabIndex > 0 && tabIndex - 1 == currentIndex)
168 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
169 else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
170 option->selectedPosition = QStyleOptionTab::NextIsSelected;
172 option->selectedPosition = QStyleOptionTab::NotAdjacent;
174 const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
175 const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
176 if (paintBeginning) {
178 option->position = QStyleOptionTab::OnlyOneTab;
180 option->position = QStyleOptionTab::Beginning;
181 }
else if (paintEnd) {
182 option->position = QStyleOptionTab::End;
184 option->position = QStyleOptionTab::Middle;
187#if QT_CONFIG(tabwidget)
188 if (
const QTabWidget *tw = qobject_cast<
const QTabWidget *>(q->parentWidget())) {
189 option->features |= QStyleOptionTab::HasFrame;
190 if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
191 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
192 if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
193 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
196 if (tab.measuringMinimum)
197 option->features |= QStyleOptionTab::MinimumSizeHint;
198 option->tabIndex = tabIndex;
202
203
204
205
206
207
208void QTabBar::initStyleOption(QStyleOptionTab *option,
int tabIndex)
const
211 d->initBasicStyleOption(option, tabIndex);
213 QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option,
this);
214 option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
215 Qt::TextShowMnemonic);
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
289
290
291
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
319
320
321
322
323
324
327
328
329
330
331
332
333
334
337
338
339
340
341
342
343
344
345
346
347
350
351
352
353
354
355
356
357
360
361
362
363
364
365
366
367
369void QTabBarPrivate::init()
372 leftB =
new QToolButton(q);
373 leftB->setObjectName(u"ScrollLeftButton"_s);
374 leftB->setAutoRepeat(
true);
375 QObjectPrivate::connect(leftB, &QToolButton::clicked,
376 this, &QTabBarPrivate::scrollTabs);
378 rightB =
new QToolButton(q);
379 rightB->setObjectName(u"ScrollRightButton"_s);
380 rightB->setAutoRepeat(
true);
381 QObjectPrivate::connect(rightB, &QToolButton::clicked,
382 this, &QTabBarPrivate::scrollTabs);
384#ifdef QT_KEYPAD_NAVIGATION
385 if (QApplicationPrivate::keypadNavigationEnabled()) {
386 leftB->setFocusPolicy(Qt::NoFocus);
387 rightB->setFocusPolicy(Qt::NoFocus);
388 q->setFocusPolicy(Qt::NoFocus);
391 q->setFocusPolicy(Qt::TabFocus);
393#if QT_CONFIG(accessibility)
394 leftB->setAccessibleName(QTabBar::tr(
"Scroll Left"));
395 rightB->setAccessibleName(QTabBar::tr(
"Scroll Right"));
397 q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
398 elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode,
nullptr, q));
399 useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows,
nullptr, q);
402int QTabBarPrivate::indexAtPos(
const QPoint &p)
const
405 if (q->tabRect(currentIndex).contains(p))
407 for (
int i = 0; i < tabList.size(); ++i)
408 if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
413void QTabBarPrivate::layoutTabs()
417 QSize size = q->size();
420 bool vertTabs = verticalTabs(shape);
421 int tabChainIndex = 0;
424 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
425 QList<QLayoutStruct> tabChain(tabList.size() + 2);
429 tabChain[tabChainIndex].init();
430 tabChain[tabChainIndex].expansive = (!expanding)
431 && (tabAlignment != Qt::AlignLeft)
432 && (tabAlignment != Qt::AlignJustify);
433 tabChain[tabChainIndex].empty =
true;
448 for (
int i = 0; i < tabList.size(); ++i) {
449 const auto tab = tabList.at(i);
454 QSize sz = q->tabSizeHint(i);
455 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
457 maxHeight = qMax(maxHeight, sz.height());
458 sz = q->minimumTabSizeHint(i);
459 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
461 tabChain[tabChainIndex].init();
462 tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
463 tabChain[tabChainIndex].minimumSize = sz.width();
464 tabChain[tabChainIndex].empty =
false;
465 tabChain[tabChainIndex].expansive =
true;
468 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
473 available = size.width();
474 maxExtent = maxHeight;
479 for (
int i = 0; i < tabList.size(); ++i) {
480 auto tab = tabList.at(i);
485 QSize sz = q->tabSizeHint(i);
486 tab->maxRect = QRect(0, y, sz.width(), sz.height());
488 maxWidth = qMax(maxWidth, sz.width());
489 sz = q->minimumTabSizeHint(i);
490 tab->minRect = QRect(0, miny, sz.width(), sz.height());
492 tabChain[tabChainIndex].init();
493 tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
494 tabChain[tabChainIndex].minimumSize = sz.height();
495 tabChain[tabChainIndex].empty =
false;
496 tabChain[tabChainIndex].expansive =
true;
499 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
504 available = size.height();
505 maxExtent = maxWidth;
509 tabChain[tabChainIndex].init();
510 tabChain[tabChainIndex].expansive = (!expanding)
511 && (tabAlignment != Qt::AlignRight)
512 && (tabAlignment != Qt::AlignJustify);
513 tabChain[tabChainIndex].empty =
true;
514 Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs);
517 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
521 for (
int i = 0; i < tabList.size(); ++i) {
522 auto tab = tabList.at(i);
528 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
530 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
532 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
535 if (useScrollButtons && tabList.size() && last > available) {
536 const QRect scrollRect = normalizedScrollRect(0);
541 QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
542 QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
543 int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
549 scrollButtonLeftRect.setHeight(scrollButtonWidth);
550 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
551 scrollButtonRightRect.setHeight(scrollButtonWidth);
552 leftB->setArrowType(Qt::UpArrow);
553 rightB->setArrowType(Qt::DownArrow);
554 }
else if (q->layoutDirection() == Qt::RightToLeft) {
555 scrollButtonRightRect.setWidth(scrollButtonWidth);
556 scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
557 scrollButtonLeftRect.setWidth(scrollButtonWidth);
558 leftB->setArrowType(Qt::RightArrow);
559 rightB->setArrowType(Qt::LeftArrow);
561 scrollButtonLeftRect.setWidth(scrollButtonWidth);
562 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
563 scrollButtonRightRect.setWidth(scrollButtonWidth);
564 leftB->setArrowType(Qt::LeftArrow);
565 rightB->setArrowType(Qt::RightArrow);
568 leftB->setGeometry(scrollButtonLeftRect);
569 leftB->setEnabled(
false);
572 rightB->setGeometry(scrollButtonRightRect);
573 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
581 q->tabLayoutChange();
584QRect QTabBarPrivate::normalizedScrollRect(
int index)
594 if (leftB->isHidden())
595 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
598 q->initStyleOption(&opt, currentIndex);
599 opt.rect = q->rect();
601 QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
602 QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
603 QRect tearLeftRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
604 QRect tearRightRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
606 if (verticalTabs(shape)) {
607 int topEdge, bottomEdge;
608 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
609 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
611 if (leftButtonIsOnTop && rightButtonIsOnTop) {
612 topEdge = scrollButtonRightRect.bottom() + 1;
613 bottomEdge = q->height();
614 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
616 bottomEdge = scrollButtonLeftRect.top();
618 topEdge = scrollButtonLeftRect.bottom() + 1;
619 bottomEdge = scrollButtonRightRect.top();
622 const auto lastTab = lastVisibleTab();
625 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
626 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != lastTab->rect.bottom() + 1 - scrollOffset;
627 if (tearTopVisible && !tearLeftRect.isNull())
628 topEdge = tearLeftRect.bottom() + 1;
629 if (tearBottomVisible && !tearRightRect.isNull())
630 bottomEdge = tearRightRect.top();
632 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
634 if (q->layoutDirection() == Qt::RightToLeft) {
635 scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
636 scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
637 tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
638 tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
641 int leftEdge, rightEdge;
642 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
643 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
645 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
646 leftEdge = scrollButtonRightRect.right() + 1;
647 rightEdge = q->width();
648 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
650 rightEdge = scrollButtonLeftRect.left();
652 leftEdge = scrollButtonLeftRect.right() + 1;
653 rightEdge = scrollButtonRightRect.left();
656 const auto lastTab = lastVisibleTab();
659 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
660 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != lastTab->rect.right() + 1 - scrollOffset;
661 if (tearLeftVisible && !tearLeftRect.isNull())
662 leftEdge = tearLeftRect.right() + 1;
663 if (tearRightVisible && !tearRightRect.isNull())
664 rightEdge = tearRightRect.left();
666 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
670int QTabBarPrivate::hoveredTabIndex()
const
679void QTabBarPrivate::makeVisible(
int index)
682 if (!validIndex(index))
685 const auto lastTab = lastVisibleTab();
686 const QRect tabRect = tabList.at(index)->rect;
687 const int oldScrollOffset = scrollOffset;
688 const bool horiz = !verticalTabs(shape);
689 const int available = horiz ? q->width() : q->height();
690 const int tabStart = horiz ? tabRect.left() : tabRect.top();
691 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
692 const int lastTabEnd = lastTab ? (horiz ? lastTab->rect.right() : lastTab->rect.bottom()) : 0;
693 const QRect scrollRect = normalizedScrollRect(index);
694 const QRect entireScrollRect = normalizedScrollRect(0);
695 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
696 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
698 if (available >= lastTabEnd) {
701 }
else if (tabStart < scrolledTabBarStart) {
703 scrollOffset = tabStart - scrollRect.left();
704 }
else if (tabEnd > scrolledTabBarEnd) {
706 scrollOffset = qMax(0, tabEnd - scrollRect.right());
707 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
709 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
712 leftB->setEnabled(scrollOffset > -scrollRect.left());
713 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
715 if (oldScrollOffset != scrollOffset) {
721void QTabBarPrivate::killSwitchTabTimer()
723 switchTabTimer.stop();
724 switchTabCurrentIndex = -1;
727void QTabBarPrivate::layoutTab(
int index)
730 Q_ASSERT(index >= 0);
732 const Tab *tab = tabList.at(index);
733 bool vertical = verticalTabs(shape);
734 if (!(tab->leftWidget || tab->rightWidget))
738 q->initStyleOption(&opt, index);
739 if (tab->leftWidget) {
740 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
741 QPoint p = rect.topLeft();
742 if ((index == pressedIndex) || paintWithOffsets) {
744 p.setY(p.y() + tab->dragOffset);
746 p.setX(p.x() + tab->dragOffset);
748 tab->leftWidget->move(p);
750 if (tab->rightWidget) {
751 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
752 QPoint p = rect.topLeft();
753 if ((index == pressedIndex) || paintWithOffsets) {
755 p.setY(p.y() + tab->dragOffset);
757 p.setX(p.x() + tab->dragOffset);
759 tab->rightWidget->move(p);
763void QTabBarPrivate::layoutWidgets(
int start)
766 for (
int i = start; i < q->count(); ++i) {
771void QTabBarPrivate::autoHideTabs()
776 q->setVisible(q->count() > 1);
779void QTabBarPrivate::closeTab()
782 QObject *object = q->sender();
784 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr, q);
785 for (
int i = 0; i < tabList.size(); ++i) {
786 if (closeSide == QTabBar::LeftSide) {
787 if (tabList.at(i)->leftWidget == object) {
792 if (tabList.at(i)->rightWidget == object) {
798 if (tabToClose != -1)
799 emit q->tabCloseRequested(tabToClose);
802void QTabBarPrivate::scrollTabs()
805 const QObject *sender = q->sender();
806 const bool horizontal = !verticalTabs(shape);
807 const QRect scrollRect = normalizedScrollRect().translated(scrollOffset, 0);
809 if (sender == leftB) {
810 for (qsizetype i = tabList.size() - 1; i >= 0; --i) {
811 const auto *tab = tabList.at(i);
814 int start = horizontal ? tab->rect.left() : tab->rect.top();
815 if (start < scrollRect.left()) {
820 }
else if (sender == rightB) {
821 for (qsizetype i = 0; i < tabList.size(); ++i) {
822 const auto *tab = tabList.at(i);
825 const auto &tabRect = tab->rect;
826 int start = horizontal ? tabRect.left() : tabRect.top();
827 int end = horizontal ? tabRect.right() : tabRect.bottom();
828 if (end > scrollRect.right() && start > scrollOffset) {
836void QTabBarPrivate::refresh()
841 if (pressedIndex != -1
843 && mouseButtons == Qt::NoButton) {
844 moveTabFinished(pressedIndex);
845 if (!validIndex(pressedIndex))
849 if (!q->isVisible()) {
853 makeVisible(currentIndex);
860
861
862QTabBar::QTabBar(QWidget* parent)
863 :QWidget(*
new QTabBarPrivate, parent, { })
871
872
878
879
880
881
882
885QTabBar::Shape QTabBar::shape()
const
891void QTabBar::setShape(Shape shape)
894 if (d->shape == shape)
901
902
903
904
905
906
907
908
910void QTabBar::setDrawBase(
bool drawBase)
913 if (d->drawBase == drawBase)
915 d->drawBase = drawBase;
919bool QTabBar::drawBase()
const
926
927
928
929int QTabBar::addTab(
const QString &text)
931 return insertTab(-1, text);
935
936
937
938
939
940int QTabBar::addTab(
const QIcon& icon,
const QString &text)
942 return insertTab(-1, icon, text);
946
947
948
949
950int QTabBar::insertTab(
int index,
const QString &text)
952 return insertTab(index, QIcon(), text);
956
957
958
959
960
961
962
963
964
965
966
967int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
970 if (!d->validIndex(index)) {
971 index = d->tabList.size();
972 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
974 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
976#ifndef QT_NO_SHORTCUT
977 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
979 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
981 if (d->tabList.size() == 1)
982 setCurrentIndex(index);
983 else if (index <= d->currentIndex)
986 if (index <= d->lastVisible)
989 d->lastVisible = index;
991 if (d->closeButtonOnTabs) {
993 initStyleOption(&opt, index);
994 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
995 QAbstractButton *closeButton =
new CloseButton(
this);
996 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
997 d, &QTabBarPrivate::closeTab);
998 setTabButton(index, closeSide, closeButton);
1001 for (
const auto tab : std::as_const(d->tabList)) {
1002 if (tab->lastTab >= index)
1006 if (tabAt(d->mousePosition) == index) {
1007 d->hoverIndex = index;
1008 d->hoverRect = tabRect(index);
1018
1019
1020
1021
1022void QTabBar::removeTab(
int index)
1025 if (d->validIndex(index)) {
1026 auto removedTab = d->tabList.at(index);
1027 if (d->dragInProgress)
1028 d->moveTabFinished(d->pressedIndex);
1030#ifndef QT_NO_SHORTCUT
1031 releaseShortcut(d->tabList.at(index)->shortcutId);
1033 if (removedTab->leftWidget) {
1034 removedTab->leftWidget->hide();
1035 removedTab->leftWidget->deleteLater();
1036 removedTab->leftWidget =
nullptr;
1038 if (removedTab->rightWidget) {
1039 removedTab->rightWidget->hide();
1040 removedTab->rightWidget->deleteLater();
1041 removedTab->rightWidget =
nullptr;
1044 int newIndex = removedTab->lastTab;
1045 d->tabList.removeAt(index);
1047 for (
auto tab : std::as_const(d->tabList)) {
1048 if (tab->lastTab == index)
1050 if (tab->lastTab > index)
1054 d->calculateFirstLastVisible(index,
false,
true);
1056 if (index == d->currentIndex) {
1060 d->currentIndex = -1;
1061 if (d->tabList.size() > 0) {
1062 switch(d->selectionBehaviorOnRemove) {
1063 case SelectPreviousTab:
1064 if (newIndex > index)
1066 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1069 case SelectRightTab:
1070 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1073 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1079 if (d->validIndex(newIndex)) {
1081 int bump = d->tabList.at(newIndex)->lastTab;
1082 setCurrentIndex(newIndex);
1083 d->tabList.at(newIndex)->lastTab = bump;
1086 emit currentChanged(-1);
1089 emit currentChanged(-1);
1091 }
else if (index < d->currentIndex) {
1092 setCurrentIndex(d->currentIndex - 1);
1096 if (d->hoverRect.isValid()) {
1097 update(d->hoverRect);
1098 d->hoverIndex = tabAt(d->mousePosition);
1099 if (d->validIndex(d->hoverIndex)) {
1100 d->hoverRect = tabRect(d->hoverIndex);
1101 update(d->hoverRect);
1103 d->hoverRect = QRect();
1112
1113
1114
1115bool QTabBar::isTabEnabled(
int index)
const
1118 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1119 return tab->enabled;
1124
1125
1126
1127void QTabBar::setTabEnabled(
int index,
bool enabled)
1130 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1131 tab->enabled = enabled;
1132#ifndef QT_NO_SHORTCUT
1133 setShortcutEnabled(tab->shortcutId, enabled);
1136 if (!enabled && index == d->currentIndex)
1137 setCurrentIndex(d->selectNewCurrentIndexFrom(index+1));
1138 else if (enabled && !isTabVisible(d->currentIndex))
1139 setCurrentIndex(d->selectNewCurrentIndexFrom(index));
1145
1146
1147
1148
1149bool QTabBar::isTabVisible(
int index)
const
1152 if (d->validIndex(index))
1153 return d->tabList.at(index)->visible;
1158
1159
1160
1161
1162void QTabBar::setTabVisible(
int index,
bool visible)
1165 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1166 d->layoutDirty = (visible != tab->visible);
1167 if (!d->layoutDirty)
1169 tab->visible = visible;
1170 if (tab->leftWidget)
1171 tab->leftWidget->setVisible(visible);
1172 if (tab->rightWidget)
1173 tab->rightWidget->setVisible(visible);
1174#ifndef QT_NO_SHORTCUT
1175 setShortcutEnabled(tab->shortcutId, visible);
1177 d->calculateFirstLastVisible(index, visible,
false);
1178 if (!visible && index == d->currentIndex) {
1179 const int newindex = d->selectNewCurrentIndexFrom(index+1);
1180 setCurrentIndex(newindex);
1188
1189
1190
1191QString QTabBar::tabText(
int index)
const
1194 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1200
1201
1202void QTabBar::setTabText(
int index,
const QString &text)
1205 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1206 d->textSizes.remove(tab->text);
1208#ifndef QT_NO_SHORTCUT
1209 releaseShortcut(tab->shortcutId);
1210 tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1211 setShortcutEnabled(tab->shortcutId, tab->enabled);
1218
1219
1220
1221
1222
1223QColor QTabBar::tabTextColor(
int index)
const
1226 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1227 return tab->textColor;
1232
1233
1234
1235
1236
1237
1238void QTabBar::setTabTextColor(
int index,
const QColor &color)
1241 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1242 tab->textColor = color;
1243 update(tabRect(index));
1248
1249
1250
1251QIcon QTabBar::tabIcon(
int index)
const
1254 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1260
1261
1262void QTabBar::setTabIcon(
int index,
const QIcon & icon)
1265 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1266 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1268 if (simpleIconChange)
1269 update(tabRect(index));
1275#if QT_CONFIG(tooltip)
1277
1278
1279void QTabBar::setTabToolTip(
int index,
const QString & tip)
1282 if (QTabBarPrivate::Tab *tab = d->at(index))
1287
1288
1289
1290QString QTabBar::tabToolTip(
int index)
const
1293 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1294 return tab->toolTip;
1299#if QT_CONFIG(whatsthis)
1301
1302
1303
1304
1305
1306void QTabBar::setTabWhatsThis(
int index,
const QString &text)
1309 if (QTabBarPrivate::Tab *tab = d->at(index))
1310 tab->whatsThis = text;
1314
1315
1316
1317
1318
1319QString QTabBar::tabWhatsThis(
int index)
const
1322 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1323 return tab->whatsThis;
1330
1331
1332void QTabBar::setTabData(
int index,
const QVariant & data)
1335 if (QTabBarPrivate::Tab *tab = d->at(index))
1340
1341
1342
1343QVariant QTabBar::tabData(
int index)
const
1346 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1352
1353
1354
1355QRect QTabBar::tabRect(
int index)
const
1358 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1360 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1363 QRect r = tab->rect;
1364 if (verticalTabs(d->shape))
1365 r.translate(0, -d->scrollOffset);
1367 r.translate(-d->scrollOffset, 0);
1368 if (!verticalTabs(d->shape))
1369 r = QStyle::visualRect(layoutDirection(), rect(), r);
1376
1377
1378
1379
1381int QTabBar::tabAt(
const QPoint &position)
const
1384 if (d->validIndex(d->currentIndex)
1385 && tabRect(d->currentIndex).contains(position)) {
1386 return d->currentIndex;
1388 const int max = d->tabList.size();
1389 for (
int i = 0; i < max; ++i) {
1390 if (tabRect(i).contains(position)) {
1398
1399
1400
1401
1402
1404int QTabBar::currentIndex()
const
1407 if (d->validIndex(d->currentIndex))
1408 return d->currentIndex;
1413void QTabBar::setCurrentIndex(
int index)
1416 if (d->dragInProgress && d->pressedIndex != -1)
1418 if (d->currentIndex == index)
1421 int oldIndex = d->currentIndex;
1422 if (
auto tab = d->at(index)) {
1423 d->currentIndex = index;
1429 if (tabRect(index).size() != tabSizeHint(index))
1433 d->layoutDirty =
true;
1435 d->makeVisible(index);
1436 if (d->validIndex(oldIndex)) {
1437 tab->lastTab = oldIndex;
1438 d->layoutTab(oldIndex);
1440 d->layoutTab(index);
1441#if QT_CONFIG(accessibility)
1442 if (QAccessible::isActive()) {
1444 QAccessibleEvent focusEvent(
this, QAccessible::Focus);
1445 focusEvent.setChild(index);
1446 QAccessible::updateAccessibility(&focusEvent);
1448 QAccessibleEvent selectionEvent(
this, QAccessible::Selection);
1449 selectionEvent.setChild(index);
1450 QAccessible::updateAccessibility(&selectionEvent);
1453 emit currentChanged(index);
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467QSize QTabBar::iconSize()
const
1470 if (d->iconSize.isValid())
1472 int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize,
nullptr,
this);
1473 return QSize(iconExtent, iconExtent);
1477void QTabBar::setIconSize(
const QSize &size)
1481 d->layoutDirty =
true;
1487
1488
1489
1491int QTabBar::count()
const
1494 return d->tabList.size();
1499
1500QSize QTabBar::sizeHint()
const
1504 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1506 for (
const auto tab : d->tabList) {
1508 r = r.united(tab->maxRect);
1514
1515QSize QTabBar::minimumSizeHint()
const
1519 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1520 if (!d->useScrollButtons) {
1522 for (
const auto tab : d->tabList) {
1524 r = r.united(tab->minRect);
1528 if (verticalTabs(d->shape))
1529 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1531 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1537 if (text.size() <= 3)
1540 static const auto Ellipses =
"..."_L1;
1543 case Qt::ElideRight:
1544 ret = QStringView{text}.left(2) + Ellipses;
1546 case Qt::ElideMiddle:
1547 ret = QStringView{text}.left(1) + Ellipses + QStringView{text}.right(1);
1550 ret = Ellipses + QStringView{text}.right(2);
1560
1561
1562
1564QSize QTabBar::minimumTabSizeHint(
int index)
const
1567 QTabBarPrivate::Tab *tab = d->tabList.at(index);
1568 QString oldText = tab->text;
1569 tab->text = computeElidedText(d->elideMode, oldText);
1570 tab->measuringMinimum =
true;
1571 QSize size = tabSizeHint(index);
1572 tab->text = oldText;
1573 tab->measuringMinimum =
false;
1578
1579
1580QSize QTabBar::tabSizeHint(
int index)
const
1584 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1585 QStyleOptionTab opt;
1586 d->initBasicStyleOption(&opt, index);
1587 opt.text = tab->text;
1588 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1589 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt,
this);
1590 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt,
this);
1591 const QFontMetrics fm = fontMetrics();
1593 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1594 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1596 int widgetWidth = 0;
1597 int widgetHeight = 0;
1599 if (!opt.leftButtonSize.isEmpty()) {
1601 widgetWidth += opt.leftButtonSize.width();
1602 widgetHeight += opt.leftButtonSize.height();
1604 if (!opt.rightButtonSize.isEmpty()) {
1606 widgetWidth += opt.rightButtonSize.width();
1607 widgetHeight += opt.rightButtonSize.height();
1609 if (!opt.icon.isNull())
1612 QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1613 if (it == d->textSizes.end())
1614 it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1615 const int textWidth = it.value().width();
1617 if (verticalTabs(d->shape)) {
1618 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1619 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1621 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1622 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1625 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1632
1633
1634
1635
1636
1637void QTabBar::tabInserted(
int index)
1643
1644
1645
1646
1647
1648void QTabBar::tabRemoved(
int index)
1654
1655
1656
1657
1658void QTabBar::tabLayoutChange()
1664
1665void QTabBar::showEvent(QShowEvent *)
1670 if (!d->validIndex(d->currentIndex))
1673 d->makeVisible(d->currentIndex);
1674 d->updateMacBorderMetrics();
1678
1679void QTabBar::hideEvent(QHideEvent *)
1682 d->updateMacBorderMetrics();
1686
1687bool QTabBar::event(QEvent *event)
1690 switch (event->type()) {
1691 case QEvent::HoverMove:
1692 case QEvent::HoverEnter: {
1693 QHoverEvent *he =
static_cast<QHoverEvent *>(event);
1694 d->mousePosition = he->position().toPoint();
1695 if (!d->hoverRect.contains(d->mousePosition)) {
1696 if (d->hoverRect.isValid())
1697 update(d->hoverRect);
1698 d->hoverIndex = tabAt(d->mousePosition);
1699 if (d->validIndex(d->hoverIndex)) {
1700 d->hoverRect = tabRect(d->hoverIndex);
1701 update(d->hoverRect);
1703 d->hoverRect = QRect();
1708 case QEvent::HoverLeave: {
1709 d->mousePosition = {-1, -1};
1710 if (d->hoverRect.isValid())
1711 update(d->hoverRect);
1713 d->hoverRect = QRect();
1714#if QT_CONFIG(wheelevent)
1715 d->accumulatedAngleDelta = QPoint();
1719#if QT_CONFIG(tooltip)
1720 case QEvent::ToolTip:
1721 if (
const QTabBarPrivate::Tab *tab = d->at(tabAt(
static_cast<QHelpEvent*>(event)->pos()))) {
1722 if (!tab->toolTip.isEmpty()) {
1723 QToolTip::showText(
static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip,
this);
1729#if QT_CONFIG(whatsthis)
1730 case QEvent::QEvent::QueryWhatsThis: {
1731 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()));
1732 if (!tab || tab->whatsThis.isEmpty())
1736 case QEvent::WhatsThis:
1737 if (
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()))) {
1738 if (!tab->whatsThis.isEmpty()) {
1739 QWhatsThis::showText(
static_cast<QHelpEvent*>(event)->globalPos(),
1740 tab->whatsThis,
this);
1746#ifndef QT_NO_SHORTCUT
1748 case QEvent::Shortcut: {
1749 QShortcutEvent *se =
static_cast<QShortcutEvent *>(event);
1750 for (
int i = 0; i < d->tabList.size(); ++i) {
1751 const QTabBarPrivate::Tab *tab = d->tabList.at(i);
1752 if (tab->shortcutId == se->shortcutId()) {
1761 d->updateMacBorderMetrics();
1763#if QT_CONFIG(draganddrop)
1765 case QEvent::DragEnter:
1766 if (d->changeCurrentOnDrag)
1769 case QEvent::DragMove:
1770 if (d->changeCurrentOnDrag) {
1771 const int tabIndex = tabAt(
static_cast<QDragMoveEvent *>(event)->position().toPoint());
1772 if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1773 d->switchTabCurrentIndex = tabIndex;
1774 d->switchTabTimer.start(
1775 style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay,
nullptr,
this) * 1ms,
this);
1780 case QEvent::DragLeave:
1782 d->killSwitchTabTimer();
1786 case QEvent::MouseButtonPress:
1787 case QEvent::MouseButtonRelease:
1788 case QEvent::MouseMove:
1789 d->mousePosition =
static_cast<QMouseEvent *>(event)->position().toPoint();
1790 d->mouseButtons =
static_cast<QMouseEvent *>(event)->buttons();
1796 return QWidget::event(event);
1800
1801void QTabBar::resizeEvent(QResizeEvent *)
1810 d->makeVisible(d->currentIndex);
1814
1815void QTabBar::paintEvent(QPaintEvent *)
1819 QStyleOptionTabBarBase optTabBase;
1820 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1822 QStylePainter p(
this);
1826 bool vertical = verticalTabs(d->shape);
1827 QStyleOptionTab cutTabLeft;
1828 QStyleOptionTab cutTabRight;
1829 selected = d->currentIndex;
1830 if (d->dragInProgress)
1831 selected = d->pressedIndex;
1832 const QRect scrollRect = d->normalizedScrollRect();
1834 for (
int i = 0; i < d->tabList.size(); ++i)
1835 optTabBase.tabBarRect |= tabRect(i);
1837 optTabBase.selectedTabRect = tabRect(selected);
1840 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1844 if (d->leftB->isVisible() || d->rightB->isVisible()) {
1847 QRegion buttonRegion;
1848 if (d->leftB->isVisible())
1849 buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt,
this);
1850 if (d->rightB->isVisible())
1851 buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt,
this);
1852 if (!buttonRegion.isEmpty())
1853 p.setClipRegion(QRegion(rect()) - buttonRegion);
1856 for (
int i = 0; i < d->tabList.size(); ++i) {
1857 const auto tab = d->tabList.at(i);
1860 QStyleOptionTab tabOption;
1861 initStyleOption(&tabOption, i);
1862 if (d->paintWithOffsets && tab->dragOffset != 0) {
1864 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1866 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1869 if (!(tabOption.state & QStyle::State_Enabled)) {
1870 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
1875 QRect tabRect = tab->rect;
1876 int tabStart = vertical ? tabRect.top() : tabRect.left();
1877 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1878 if (tabStart < scrollRect.left() + d->scrollOffset) {
1880 cutTabLeft = tabOption;
1881 }
else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1883 cutTabRight = tabOption;
1887 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1888 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1891 optTabBase.tabBarRect |= tabOption.rect;
1895 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1899 if (selected >= 0) {
1900 QStyleOptionTab tabOption;
1901 const auto tab = d->tabList.at(selected);
1902 initStyleOption(&tabOption, selected);
1904 if (d->paintWithOffsets && tab->dragOffset != 0) {
1907 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1910 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1912 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1916 const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr,
this);
1917 const QRect &movingRect = verticalTabs(d->shape)
1918 ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
1919 : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
1923 if (d->dragInProgress)
1924 d->movingTab->setGeometry(movingRect);
1926 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1930 if (d->leftB->isVisible() && cutLeft >= 0) {
1931 cutTabLeft.rect = rect();
1932 cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft,
this);
1933 p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1936 if (d->rightB->isVisible() && cutRight >= 0) {
1937 cutTabRight.rect = rect();
1938 cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight,
this);
1939 p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
1944
1945
1946
1947void QTabBarPrivate::calculateFirstLastVisible(
int index,
bool visible,
bool remove)
1950 firstVisible = qMin(index, firstVisible);
1951 lastVisible = qMax(index, lastVisible);
1953 if (remove || (index == firstVisible)) {
1955 for (
int i = 0; i < tabList.size(); ++i) {
1956 if (tabList.at(i)->visible) {
1962 if (remove || (index == lastVisible)) {
1964 for (
int i = tabList.size() - 1; i >= 0; --i) {
1965 if (tabList.at(i)->visible) {
1975
1976
1977
1978
1979int QTabBarPrivate::selectNewCurrentIndexFrom(
int fromIndex)
1982 for (
int i = fromIndex; i < tabList.size(); ++i) {
1983 if (at(i)->visible && at(i)->enabled) {
1989 for (
int i = fromIndex-1; i > -1; --i) {
1990 if (at(i)->visible && at(i)->enabled) {
2001
2002
2003int QTabBarPrivate::calculateNewPosition(
int from,
int to,
int index)
const
2008 int start = qMin(from, to);
2009 int end = qMax(from, to);
2010 if (index >= start && index <= end)
2011 index += (from < to) ? -1 : 1;
2016
2017
2018
2019
2020
2021void QTabBar::moveTab(
int from,
int to)
2025 || !d->validIndex(from)
2026 || !d->validIndex(to))
2029 auto &fromTab = *d->tabList.at(from);
2030 auto &toTab = *d->tabList.at(to);
2032 bool vertical = verticalTabs(d->shape);
2033 int oldPressedPosition = 0;
2034 if (d->pressedIndex != -1) {
2036 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2037 : d->tabList.at(d->pressedIndex)->rect.x();
2041 int start = qMin(from, to);
2042 int end = qMax(from, to);
2043 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2046 bool rtl = isRightToLeft();
2047 for (
int i = start; i <= end; ++i) {
2050 auto &tab = *d->tabList.at(i);
2052 tab.rect.moveTop(tab.rect.y() + width);
2054 tab.rect.moveLeft(tab.rect.x() + width);
2056 if (rtl && !vertical)
2058 if (tab.dragOffset != 0)
2059 tab.dragOffset += (direction * width);
2064 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2066 fromTab.rect.moveTop(toTab.rect.top() - width);
2069 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2071 fromTab.rect.moveLeft(toTab.rect.left() - width);
2075 d->tabList.move(from, to);
2078 for (
const auto tab : std::as_const(d->tabList))
2079 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2082 int previousIndex = d->currentIndex;
2083 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2086 if (d->pressedIndex != -1) {
2087 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2088 const auto pressedTab = d->tabList.at(d->pressedIndex);
2089 int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
2090 int diff = oldPressedPosition - newPressedPosition;
2091 if (isRightToLeft() && !vertical)
2094 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2096 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2099 d->layoutWidgets(start);
2101 emit tabMoved(from, to);
2102 if (previousIndex != d->currentIndex)
2103 emit currentChanged(d->currentIndex);
2104 emit tabLayoutChange();
2107void QTabBarPrivate::slide(
int from,
int to)
2111 || !validIndex(from)
2114 bool vertical = verticalTabs(shape);
2115 int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
2116 q->setUpdatesEnabled(
false);
2117 q->moveTab(from, to);
2118 q->setUpdatesEnabled(
true);
2119 int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
2120 int length = postLocation - preLocation;
2121 tabList.at(to)->dragOffset -= length;
2122 tabList.at(to)->startAnimation(
this, ANIMATION_DURATION);
2125void QTabBarPrivate::moveTab(
int index,
int offset)
2127 if (!validIndex(index))
2129 tabList.at(index)->dragOffset = offset;
2135
2136void QTabBar::mousePressEvent(QMouseEvent *event)
2140 const QPoint pos = event->position().toPoint();
2141 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2142 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2143 if (!isEventInCornerButtons) {
2144 const int index = d->indexAtPos(pos);
2145 emit tabBarClicked(index);
2148 if (event->button() != Qt::LeftButton) {
2153 if (d->pressedIndex != -1 && d->movable)
2154 d->moveTabFinished(d->pressedIndex);
2156 d->pressedIndex = d->indexAtPos(event->position().toPoint());
2158 if (d->validIndex(d->pressedIndex)) {
2159 QStyleOptionTabBarBase optTabBase;
2160 optTabBase.initFrom(
this);
2161 optTabBase.documentMode = d->documentMode;
2162 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this))
2163 setCurrentIndex(d->pressedIndex);
2165 repaint(tabRect(d->pressedIndex));
2167 d->dragStartPosition = event->position().toPoint();
2173
2174void QTabBar::mouseMoveEvent(QMouseEvent *event)
2179 if (d->pressedIndex != -1
2180 && event->buttons() == Qt::NoButton)
2181 d->moveTabFinished(d->pressedIndex);
2184 if (!d->dragInProgress && d->pressedIndex != -1) {
2185 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2186 d->dragInProgress =
true;
2187 d->setupMovableTab();
2191 if (event->buttons() == Qt::LeftButton
2192 && d->dragInProgress
2193 && d->validIndex(d->pressedIndex)) {
2194 bool vertical = verticalTabs(d->shape);
2197 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2199 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2201 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2203 QRect startingRect = tabRect(d->pressedIndex);
2205 startingRect.moveTop(startingRect.y() + dragDistance);
2207 startingRect.moveLeft(startingRect.x() + dragDistance);
2210 if (dragDistance < 0)
2211 overIndex = tabAt(startingRect.topLeft());
2213 overIndex = tabAt(startingRect.topRight());
2215 if (overIndex != d->pressedIndex && overIndex != -1) {
2217 if (isRightToLeft() && !vertical)
2219 if (dragDistance < 0) {
2223 for (
int i = d->pressedIndex;
2224 offset > 0 ? i < overIndex : i > overIndex;
2226 QRect overIndexRect = tabRect(overIndex);
2227 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2228 if (dragDistance > needsToBeOver)
2229 d->slide(i + offset, d->pressedIndex);
2233 if (d->pressedIndex != -1)
2234 d->layoutTab(d->pressedIndex);
2240 if (event->buttons() != Qt::LeftButton) {
2246void QTabBarPrivate::setupMovableTab()
2250 movingTab =
new QMovableTabWidget(q);
2252 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr ,q);
2253 QRect grabRect = q->tabRect(pressedIndex);
2254 if (verticalTabs(shape))
2255 grabRect.adjust(0, -taboverlap, 0, taboverlap);
2257 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2259 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2260 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2261 grabImage.fill(Qt::transparent);
2262 QStylePainter p(&grabImage, q);
2264 QStyleOptionTab tab;
2265 q->initStyleOption(&tab, pressedIndex);
2266 tab.position = QStyleOptionTab::Moving;
2267 if (verticalTabs(shape))
2268 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2270 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2271 p.drawControl(QStyle::CE_TabBarTab, tab);
2274 movingTab->setPixmap(grabImage);
2275 movingTab->setGeometry(grabRect);
2279 const auto &pressedTab = *tabList.at(pressedIndex);
2280 if (pressedTab.leftWidget)
2281 pressedTab.leftWidget->raise();
2282 if (pressedTab.rightWidget)
2283 pressedTab.rightWidget->raise();
2288 movingTab->setVisible(
true);
2291void QTabBarPrivate::moveTabFinished(
int index)
2294 bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
2295 bool allAnimationsFinished =
true;
2296#if QT_CONFIG(animation)
2297 for (
const auto tab : std::as_const(tabList)) {
2298 if (tab->animation && tab->animation->state() == QAbstractAnimation::Running) {
2299 allAnimationsFinished =
false;
2304 if (allAnimationsFinished && cleanup) {
2306 movingTab->setVisible(
false);
2307 for (
auto tab : std::as_const(tabList)) {
2308 tab->dragOffset = 0;
2310 if (pressedIndex != -1 && movable) {
2312 dragInProgress =
false;
2313 dragStartPosition = QPoint();
2317 if (!validIndex(index))
2319 tabList.at(index)->dragOffset = 0;
2325
2326void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2329 if (event->button() != Qt::LeftButton) {
2334 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2335 int length = d->tabList.at(d->pressedIndex)->dragOffset;
2336 int width = verticalTabs(d->shape)
2337 ? tabRect(d->pressedIndex).height()
2338 : tabRect(d->pressedIndex).width();
2339 int duration = qMin(ANIMATION_DURATION,
2340 (qAbs(length) * ANIMATION_DURATION) / width);
2341 d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
2342 d->dragInProgress =
false;
2343 d->movingTab->setVisible(
false);
2344 d->dragStartPosition = QPoint();
2348 int oldPressedIndex = d->pressedIndex;
2349 int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
2350 d->pressedIndex = -1;
2351 QStyleOptionTabBarBase optTabBase;
2352 optTabBase.initFrom(
this);
2353 optTabBase.documentMode = d->documentMode;
2354 const bool selectOnRelease =
2355 (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this) == QEvent::MouseButtonRelease);
2356 if (selectOnRelease)
2358 if (d->validIndex(oldPressedIndex))
2359 update(tabRect(oldPressedIndex));
2363
2364void QTabBar::mouseDoubleClickEvent(QMouseEvent *event)
2367 const QPoint pos = event->position().toPoint();
2368 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2369 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2370 if (!isEventInCornerButtons)
2371 emit tabBarDoubleClicked(tabAt(pos));
2373 mousePressEvent(event);
2377
2378void QTabBar::keyPressEvent(QKeyEvent *event)
2381 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
2385 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
2386 d->setCurrentNextEnabledIndex(offset);
2390
2391#if QT_CONFIG(wheelevent)
2392void QTabBar::wheelEvent(QWheelEvent *event)
2395 if (style()->styleHint(QStyle::SH_TabBar_AllowWheelScrolling,
nullptr,
this)) {
2396 const bool wheelVertical = qAbs(event->angleDelta().y()) > qAbs(event->angleDelta().x());
2397 const bool tabsVertical = verticalTabs(d->shape);
2398 if (event->device()->capabilities().testFlag(QInputDevice::Capability::PixelScroll)) {
2402 if (tabsVertical == wheelVertical)
2403 delta = wheelVertical ? event->pixelDelta().y() : event->pixelDelta().x();
2404 if (layoutDirection() == Qt::RightToLeft)
2406 if (delta && d->validIndex(d->lastVisible)) {
2407 const int oldScrollOffset = d->scrollOffset;
2408 const QRect lastTabRect = d->tabList.at(d->lastVisible)->rect;
2409 const QRect scrollRect = d->normalizedScrollRect(d->lastVisible);
2410 int scrollRectExtent = scrollRect.right();
2411 if (!d->leftB->isVisible())
2412 scrollRectExtent += tabsVertical ? d->leftB->height() : d->leftB->width();
2413 if (!d->rightB->isVisible())
2414 scrollRectExtent += tabsVertical ? d->rightB->height() : d->rightB->width();
2416 const int maxScrollOffset = qMax((tabsVertical ?
2417 lastTabRect.bottom() :
2418 lastTabRect.right()) - scrollRectExtent, 0);
2419 d->scrollOffset = qBound(0, d->scrollOffset - delta, maxScrollOffset);
2420 d->leftB->setEnabled(d->scrollOffset > -scrollRect.left());
2421 d->rightB->setEnabled(maxScrollOffset > d->scrollOffset);
2422 if (oldScrollOffset != d->scrollOffset) {
2429 d->accumulatedAngleDelta += event->angleDelta();
2430 const int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
2431 const int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
2433 if (xSteps > 0 || ySteps > 0) {
2435 d->accumulatedAngleDelta = QPoint();
2436 }
else if (xSteps < 0 || ySteps < 0) {
2438 d->accumulatedAngleDelta = QPoint();
2440 const int oldCurrentIndex = d->currentIndex;
2441 d->setCurrentNextEnabledIndex(offset);
2442 if (oldCurrentIndex != d->currentIndex) {
2447 QWidget::wheelEvent(event);
2452void QTabBarPrivate::setCurrentNextEnabledIndex(
int offset)
2455 for (
int index = currentIndex + offset; validIndex(index); index += offset) {
2456 if (tabList.at(index)->enabled && tabList.at(index)->visible) {
2457 q->setCurrentIndex(index);
2464
2465void QTabBar::changeEvent(QEvent *event)
2468 switch (event->type()) {
2469 case QEvent::StyleChange:
2470 if (!d->elideModeSetByUser)
2471 d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode,
nullptr,
this));
2472 if (!d->useScrollButtonsSetByUser)
2473 d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows,
nullptr,
this);
2475 case QEvent::FontChange:
2476 d->textSizes.clear();
2483 QWidget::changeEvent(event);
2487
2488
2489void QTabBar::timerEvent(QTimerEvent *event)
2492 if (event->id() == d->switchTabTimer.id()) {
2493 d->switchTabTimer.stop();
2494 setCurrentIndex(d->switchTabCurrentIndex);
2495 d->switchTabCurrentIndex = -1;
2497 QWidget::timerEvent(event);
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2513Qt::TextElideMode QTabBar::elideMode()
const
2516 return d->elideMode;
2519void QTabBar::setElideMode(Qt::TextElideMode mode)
2522 d->elideMode = mode;
2523 d->elideModeSetByUser =
true;
2524 d->textSizes.clear();
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541bool QTabBar::usesScrollButtons()
const
2543 return d_func()->useScrollButtons;
2546void QTabBar::setUsesScrollButtons(
bool useButtons)
2549 d->useScrollButtonsSetByUser =
true;
2550 if (d->useScrollButtons == useButtons)
2552 d->useScrollButtons = useButtons;
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2570bool QTabBar::tabsClosable()
const
2573 return d->closeButtonOnTabs;
2576void QTabBar::setTabsClosable(
bool closable)
2579 if (d->closeButtonOnTabs == closable)
2581 d->closeButtonOnTabs = closable;
2582 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
2584 for (
auto tab : std::as_const(d->tabList)) {
2585 if (closeSide == LeftSide && tab->leftWidget) {
2586 tab->leftWidget->deleteLater();
2587 tab->leftWidget =
nullptr;
2589 if (closeSide == RightSide && tab->rightWidget) {
2590 tab->rightWidget->deleteLater();
2591 tab->rightWidget =
nullptr;
2595 bool newButtons =
false;
2596 for (
int i = 0; i < d->tabList.size(); ++i) {
2597 if (tabButton(i, closeSide))
2600 QAbstractButton *closeButton =
new CloseButton(
this);
2601 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
2602 d, &QTabBarPrivate::closeTab);
2603 setTabButton(i, closeSide, closeButton);
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2639
2640
2641
2642
2643
2644
2645
2646
2647
2650QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove()
const
2653 return d->selectionBehaviorOnRemove;
2656void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2659 d->selectionBehaviorOnRemove = behavior;
2663
2664
2665
2666
2667
2668
2669
2670
2672bool QTabBar::expanding()
const
2675 return d->expanding;
2678void QTabBar::setExpanding(
bool enabled)
2681 if (d->expanding == enabled)
2683 d->expanding = enabled;
2688
2689
2690
2691
2692
2693
2694
2695
2697bool QTabBar::isMovable()
const
2703void QTabBar::setMovable(
bool movable)
2706 d->movable = movable;
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721bool QTabBar::documentMode()
const
2723 return d_func()->documentMode;
2726void QTabBar::setDocumentMode(
bool enabled)
2730 d->documentMode = enabled;
2731 d->updateMacBorderMetrics();
2735
2736
2737
2738
2739
2740
2741
2742
2743
2745bool QTabBar::autoHide()
const
2751void QTabBar::setAutoHide(
bool hide)
2754 if (d->autoHide == hide)
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2776bool QTabBar::changeCurrentOnDrag()
const
2779 return d->changeCurrentOnDrag;
2782void QTabBar::setChangeCurrentOnDrag(
bool change)
2785 d->changeCurrentOnDrag = change;
2787 d->killSwitchTabTimer();
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804void QTabBar::setTabButton(
int index, ButtonPosition position, QWidget *widget)
2807 if (index < 0 || index >= d->tabList.size())
2810 widget->setParent(
this);
2815 auto &tab = *d->tabList.at(index);
2816 if (position == LeftSide) {
2818 tab.leftWidget->hide();
2819 tab.leftWidget = widget;
2821 if (tab.rightWidget)
2822 tab.rightWidget->hide();
2823 tab.rightWidget = widget;
2831
2832
2833
2834QWidget *QTabBar::tabButton(
int index, ButtonPosition position)
const
2837 if (
const auto tab = d->at(index)) {
2838 return position == LeftSide ? tab->leftWidget
2844#if QT_CONFIG(accessibility)
2846
2847
2848void QTabBar::setAccessibleTabName(
int index,
const QString &name)
2851 if (QTabBarPrivate::Tab *tab = d->at(index)) {
2852 tab->accessibleName = name;
2853 QAccessibleEvent event(
this, QAccessible::NameChanged);
2854 event.setChild(index);
2855 QAccessible::updateAccessibility(&event);
2860
2861
2862
2863QString QTabBar::accessibleTabName(
int index)
const
2866 if (
const QTabBarPrivate::Tab *tab = d->at(index))
2867 return tab->accessibleName;
2872CloseButton::CloseButton(
QWidget *parent)
2873 : QAbstractButton(parent)
2875 setFocusPolicy(Qt::NoFocus);
2877 setCursor(Qt::ArrowCursor);
2879#if QT_CONFIG(tooltip)
2880 setToolTip(tr(
"Close Tab"));
2885QSize CloseButton::sizeHint()
const
2888 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth,
nullptr,
this);
2889 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight,
nullptr,
this);
2890 return QSize(width, height);
2893void CloseButton::enterEvent(QEnterEvent *event)
2897 QAbstractButton::enterEvent(event);
2900void CloseButton::leaveEvent(QEvent *event)
2904 QAbstractButton::leaveEvent(event);
2907void CloseButton::paintEvent(QPaintEvent *)
2912 opt.state |= QStyle::State_AutoRaise;
2913 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2914 opt.state |= QStyle::State_Raised;
2916 opt.state |= QStyle::State_On;
2918 opt.state |= QStyle::State_Sunken;
2920 if (
const QTabBar *tb = qobject_cast<
const QTabBar *>(parent())) {
2921 int index = tb->currentIndex();
2922 QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr, tb);
2923 if (tb->tabButton(index, position) ==
this)
2924 opt.state |= QStyle::State_Selected;
2927 style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p,
this);
2930#if QT_CONFIG(animation)
2931void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(
const QVariant ¤t)
2933 priv->moveTab(priv->tabList.indexOf(tab), current.toInt());
2936void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
2938 if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(tab));
2944#include "moc_qtabbar.cpp"
2945#include "qtabbar.moc"
static QString computeElidedText(Qt::TextElideMode mode, const QString &text)