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;
56 void setParentClipRect(
const QRect &clipRect)
58 m_parentClipRect = clipRect;
62 QRect m_parentClipRect;
66QMovableTabWidget::QMovableTabWidget(QWidget *parent)
71void QMovableTabWidget::setPixmap(
const QPixmap &pixmap)
77void QMovableTabWidget::paintEvent(QPaintEvent *e)
81 p.drawPixmap(0, 0, m_pixmap);
84void QTabBarPrivate::updateMacBorderMetrics()
86#if defined(Q_OS_MACOS)
97 QPoint windowPos = q->mapTo(q->window(), QPoint(0,0));
98 upper = windowPos.y();
99 int tabStripHeight = q->tabSizeHint(0).height();
101 lower = upper + tabStripHeight + pixelTweak;
107 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
108 if (!nativeInterface)
110 quintptr identifier =
reinterpret_cast<quintptr>(q);
113 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
114 nativeInterface->nativeResourceFunctionForIntegration(
"registerContentBorderArea");
117 typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier,
int upper,
int lower);
118 (
reinterpret_cast<RegisterContentBorderAreaFunction>(QFunctionPointer(function)))(
119 q->window()->windowHandle(), identifier, upper, lower);
122 function = nativeInterface->nativeResourceFunctionForIntegration(
"setContentBorderAreaEnabled");
125 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier,
bool enable);
126 (
reinterpret_cast<SetContentBorderAreaEnabledFunction>(QFunctionPointer(function)))(
127 q->window()->windowHandle(), identifier, q->isVisible());
132
133
134
135
137void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option,
int tabIndex)
const
140 const int totalTabs = tabList.size();
142 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
145 const QTabBarPrivate::Tab &tab = *tabList.at(tabIndex);
147 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
148 option->rect = q->tabRect(tabIndex);
149 const bool isCurrent = tabIndex == currentIndex;
151 if (tabIndex == pressedIndex)
152 option->state |= QStyle::State_Sunken;
154 option->state |= QStyle::State_Selected;
155 if (isCurrent && q->hasFocus())
156 option->state |= QStyle::State_HasFocus;
158 option->state &= ~QStyle::State_Enabled;
159 if (q->isActiveWindow())
160 option->state |= QStyle::State_Active;
161 if (!dragInProgress && option->rect == hoverRect)
162 option->state |= QStyle::State_MouseOver;
163 option->shape = shape;
164 option->text = tab.text;
166 if (tab.textColor.isValid())
167 option->palette.setColor(q->foregroundRole(), tab.textColor);
168 option->icon = tab.icon;
169 option->iconSize = q->iconSize();
171 option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
172 option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
173 option->documentMode = documentMode;
175 if (tabIndex > 0 && tabIndex - 1 == currentIndex)
176 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
177 else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
178 option->selectedPosition = QStyleOptionTab::NextIsSelected;
180 option->selectedPosition = QStyleOptionTab::NotAdjacent;
182 const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
183 const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
184 if (paintBeginning) {
186 option->position = QStyleOptionTab::OnlyOneTab;
188 option->position = QStyleOptionTab::Beginning;
189 }
else if (paintEnd) {
190 option->position = QStyleOptionTab::End;
192 option->position = QStyleOptionTab::Middle;
195#if QT_CONFIG(tabwidget)
196 if (
const QTabWidget *tw = qobject_cast<
const QTabWidget *>(q->parentWidget())) {
197 option->features |= QStyleOptionTab::HasFrame;
198 if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
199 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
200 if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
201 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
204 if (tab.measuringMinimum)
205 option->features |= QStyleOptionTab::MinimumSizeHint;
206 option->tabIndex = tabIndex;
210
211
212
213
214
215
216void QTabBar::initStyleOption(QStyleOptionTab *option,
int tabIndex)
const
219 d->initBasicStyleOption(option, tabIndex);
221 QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option,
this);
222 option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
223 Qt::TextShowMnemonic);
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
292
293
294
295
296
297
298
299
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
327
328
329
330
331
332
335
336
337
338
339
340
341
342
345
346
347
348
349
350
351
352
353
354
355
358
359
360
361
362
363
364
365
368
369
370
371
372
373
374
375
377void QTabBarPrivate::init()
380 leftB =
new QToolButton(q);
381 leftB->setObjectName(u"ScrollLeftButton"_s);
382 leftB->setAutoRepeat(
true);
383 QObjectPrivate::connect(leftB, &QToolButton::clicked,
384 this, &QTabBarPrivate::scrollTabs);
386 rightB =
new QToolButton(q);
387 rightB->setObjectName(u"ScrollRightButton"_s);
388 rightB->setAutoRepeat(
true);
389 QObjectPrivate::connect(rightB, &QToolButton::clicked,
390 this, &QTabBarPrivate::scrollTabs);
392#ifdef QT_KEYPAD_NAVIGATION
393 if (QApplicationPrivate::keypadNavigationEnabled()) {
394 leftB->setFocusPolicy(Qt::NoFocus);
395 rightB->setFocusPolicy(Qt::NoFocus);
396 q->setFocusPolicy(Qt::NoFocus);
399 q->setFocusPolicy(Qt::TabFocus);
401#if QT_CONFIG(accessibility)
402 leftB->setAccessibleName(QTabBar::tr(
"Scroll Left"));
403 rightB->setAccessibleName(QTabBar::tr(
"Scroll Right"));
405 q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
406 elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode,
nullptr, q));
407 useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows,
nullptr, q);
410int QTabBarPrivate::indexAtPos(
const QPoint &p)
const
413 if (q->tabRect(currentIndex).contains(p))
415 for (
int i = 0; i < tabList.size(); ++i)
416 if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
421void QTabBarPrivate::layoutTabs()
425 QSize size = q->size();
428 bool vertTabs = verticalTabs(shape);
429 int tabChainIndex = 0;
432 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
433 QList<QLayoutStruct> tabChain(tabList.size() + 2);
437 tabChain[tabChainIndex].init();
438 tabChain[tabChainIndex].expansive = (!expanding)
439 && (tabAlignment != Qt::AlignLeft)
440 && (tabAlignment != Qt::AlignJustify);
441 tabChain[tabChainIndex].empty =
true;
456 for (
int i = 0; i < tabList.size(); ++i) {
457 const auto tab = tabList.at(i);
462 QSize sz = q->tabSizeHint(i);
463 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
465 maxHeight = qMax(maxHeight, sz.height());
466 sz = q->minimumTabSizeHint(i);
467 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
469 tabChain[tabChainIndex].init();
470 tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
471 tabChain[tabChainIndex].minimumSize = sz.width();
472 tabChain[tabChainIndex].empty =
false;
473 tabChain[tabChainIndex].expansive =
true;
476 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
481 available = size.width();
482 maxExtent = maxHeight;
487 for (
int i = 0; i < tabList.size(); ++i) {
488 auto tab = tabList.at(i);
493 QSize sz = q->tabSizeHint(i);
494 tab->maxRect = QRect(0, y, sz.width(), sz.height());
496 maxWidth = qMax(maxWidth, sz.width());
497 sz = q->minimumTabSizeHint(i);
498 tab->minRect = QRect(0, miny, sz.width(), sz.height());
500 tabChain[tabChainIndex].init();
501 tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
502 tabChain[tabChainIndex].minimumSize = sz.height();
503 tabChain[tabChainIndex].empty =
false;
504 tabChain[tabChainIndex].expansive =
true;
507 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
512 available = size.height();
513 maxExtent = maxWidth;
517 tabChain[tabChainIndex].init();
518 tabChain[tabChainIndex].expansive = (!expanding)
519 && (tabAlignment != Qt::AlignRight)
520 && (tabAlignment != Qt::AlignJustify);
521 tabChain[tabChainIndex].empty =
true;
522 Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs);
525 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
529 for (
int i = 0; i < tabList.size(); ++i) {
530 auto tab = tabList.at(i);
536 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
538 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
540 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
543 if (useScrollButtons && tabList.size() && last > available) {
544 const QRect scrollRect = normalizedScrollRect(0);
549 QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
550 QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
551 int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
557 scrollButtonLeftRect.setHeight(scrollButtonWidth);
558 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
559 scrollButtonRightRect.setHeight(scrollButtonWidth);
560 leftB->setArrowType(Qt::UpArrow);
561 rightB->setArrowType(Qt::DownArrow);
562 }
else if (q->layoutDirection() == Qt::RightToLeft) {
563 scrollButtonRightRect.setWidth(scrollButtonWidth);
564 scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
565 scrollButtonLeftRect.setWidth(scrollButtonWidth);
566 leftB->setArrowType(Qt::RightArrow);
567 rightB->setArrowType(Qt::LeftArrow);
569 scrollButtonLeftRect.setWidth(scrollButtonWidth);
570 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
571 scrollButtonRightRect.setWidth(scrollButtonWidth);
572 leftB->setArrowType(Qt::LeftArrow);
573 rightB->setArrowType(Qt::RightArrow);
576 leftB->setGeometry(scrollButtonLeftRect);
577 leftB->setEnabled(
false);
580 rightB->setGeometry(scrollButtonRightRect);
581 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
589 q->tabLayoutChange();
592QRect QTabBarPrivate::normalizedScrollRect(
int index)
602 if (leftB->isHidden())
603 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
606 q->initStyleOption(&opt, currentIndex);
607 opt.rect = q->rect();
609 const auto style = q->style();
610 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
611 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
612 QRect tearLeftRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
613 QRect tearRightRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
615 if (verticalTabs(shape)) {
616 int topEdge, bottomEdge;
617 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
618 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
620 if (leftButtonIsOnTop && rightButtonIsOnTop) {
621 topEdge = scrollButtonRightRect.bottom() + 1;
622 bottomEdge = q->height();
623 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
625 bottomEdge = scrollButtonLeftRect.top();
627 topEdge = scrollButtonLeftRect.bottom() + 1;
628 bottomEdge = scrollButtonRightRect.top();
631 const auto lastTab = lastVisibleTab();
634 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
635 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != lastTab->rect.bottom() + 1 - scrollOffset;
636 if (tearTopVisible && !tearLeftRect.isNull())
637 topEdge = tearLeftRect.bottom() + 1;
638 if (tearBottomVisible && !tearRightRect.isNull())
639 bottomEdge = tearRightRect.top();
641 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
643 if (q->layoutDirection() == Qt::RightToLeft) {
644 scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
645 scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
646 tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
647 tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
650 int leftEdge, rightEdge;
651 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
652 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
654 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
655 leftEdge = scrollButtonRightRect.right() + 1;
656 rightEdge = q->width();
657 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
659 rightEdge = scrollButtonLeftRect.left();
661 leftEdge = scrollButtonLeftRect.right() + 1;
662 rightEdge = scrollButtonRightRect.left();
665 const auto lastTab = lastVisibleTab();
668 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
669 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != lastTab->rect.right() + 1 - scrollOffset;
670 if (tearLeftVisible && !tearLeftRect.isNull())
671 leftEdge = tearLeftRect.right() + 1;
672 if (tearRightVisible && !tearRightRect.isNull())
673 rightEdge = tearRightRect.left();
675 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
679int QTabBarPrivate::hoveredTabIndex()
const
688void QTabBarPrivate::makeVisible(
int index)
691 if (!validIndex(index))
694 const auto lastTab = lastVisibleTab();
695 const QRect tabRect = tabList.at(index)->rect;
696 const int oldScrollOffset = scrollOffset;
697 const bool horiz = !verticalTabs(shape);
698 const int available = horiz ? q->width() : q->height();
699 const int tabStart = horiz ? tabRect.left() : tabRect.top();
700 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
701 const int lastTabEnd = lastTab ? (horiz ? lastTab->rect.right() : lastTab->rect.bottom()) : 0;
702 const QRect scrollRect = normalizedScrollRect(index);
703 const QRect entireScrollRect = normalizedScrollRect(0);
704 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
705 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
707 if (available >= lastTabEnd) {
710 }
else if (tabStart < scrolledTabBarStart) {
712 scrollOffset = tabStart - scrollRect.left();
713 }
else if (tabEnd > scrolledTabBarEnd) {
715 scrollOffset = qMax(0, tabEnd - scrollRect.right());
716 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
718 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
721 leftB->setEnabled(scrollOffset > -scrollRect.left());
722 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
724 if (oldScrollOffset != scrollOffset) {
730void QTabBarPrivate::killSwitchTabTimer()
732 switchTabTimer.stop();
733 switchTabCurrentIndex = -1;
736void QTabBarPrivate::layoutTab(
int index)
739 Q_ASSERT(index >= 0);
741 const Tab *tab = tabList.at(index);
742 bool vertical = verticalTabs(shape);
743 if (!(tab->leftWidget || tab->rightWidget))
747 q->initStyleOption(&opt, index);
748 if (tab->leftWidget) {
749 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
750 QPoint p = rect.topLeft();
751 if (index == pressedIndex) {
753 p.setY(p.y() + tab->dragOffset);
755 p.setX(p.x() + tab->dragOffset);
757 tab->leftWidget->move(p);
759 if (tab->rightWidget) {
760 QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
761 QPoint p = rect.topLeft();
762 if (index == pressedIndex) {
764 p.setY(p.y() + tab->dragOffset);
766 p.setX(p.x() + tab->dragOffset);
768 tab->rightWidget->move(p);
772void QTabBarPrivate::layoutWidgets(
int start)
775 for (
int i = start; i < q->count(); ++i) {
780void QTabBarPrivate::autoHideTabs()
785 q->setVisible(q->count() > 1);
788void QTabBarPrivate::closeTab()
791 QObject *object = q->sender();
793 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr, q);
794 for (
int i = 0; i < tabList.size(); ++i) {
795 if (closeSide == QTabBar::LeftSide) {
796 if (tabList.at(i)->leftWidget == object) {
801 if (tabList.at(i)->rightWidget == object) {
807 if (tabToClose != -1)
808 emit q->tabCloseRequested(tabToClose);
811void QTabBarPrivate::scrollTabs()
814 const QObject *sender = q->sender();
815 const bool horizontal = !verticalTabs(shape);
816 const QRect scrollRect = normalizedScrollRect().translated(scrollOffset, 0);
818 if (sender == leftB) {
819 for (qsizetype i = tabList.size() - 1; i >= 0; --i) {
820 const auto *tab = tabList.at(i);
823 int start = horizontal ? tab->rect.left() : tab->rect.top();
824 if (start < scrollRect.left()) {
829 }
else if (sender == rightB) {
830 for (qsizetype i = 0; i < tabList.size(); ++i) {
831 const auto *tab = tabList.at(i);
834 const auto &tabRect = tab->rect;
835 int start = horizontal ? tabRect.left() : tabRect.top();
836 int end = horizontal ? tabRect.right() : tabRect.bottom();
837 if (end > scrollRect.right() && start > scrollRect.left()) {
845void QTabBarPrivate::refresh()
850 if (pressedIndex != -1
852 && mouseButtons == Qt::NoButton) {
853 moveTabFinished(pressedIndex);
854 if (!validIndex(pressedIndex))
858 if (!q->isVisible()) {
862 makeVisible(currentIndex);
869
870
871QTabBar::QTabBar(QWidget* parent)
872 :QWidget(*
new QTabBarPrivate, parent, { })
880
881
887
888
889
890
891
894QTabBar::Shape QTabBar::shape()
const
900void QTabBar::setShape(Shape shape)
903 if (d->shape == shape)
910
911
912
913
914
915
916
917
919void QTabBar::setDrawBase(
bool drawBase)
922 if (d->drawBase == drawBase)
924 d->drawBase = drawBase;
928bool QTabBar::drawBase()
const
935
936
937
938int QTabBar::addTab(
const QString &text)
940 return insertTab(-1, text);
944
945
946
947
948
949int QTabBar::addTab(
const QIcon& icon,
const QString &text)
951 return insertTab(-1, icon, text);
955
956
957
958
959int QTabBar::insertTab(
int index,
const QString &text)
961 return insertTab(index, QIcon(), text);
965
966
967
968
969
970
971
972
973
974
975
976int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
979 if (!d->validIndex(index)) {
980 index = d->tabList.size();
981 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
983 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
985#ifndef QT_NO_SHORTCUT
986 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
988 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
990 if (d->tabList.size() == 1)
991 setCurrentIndex(index);
992 else if (index <= d->currentIndex)
995 if (index <= d->lastVisible)
998 d->lastVisible = index;
1000 if (d->closeButtonOnTabs) {
1001 QStyleOptionTab opt;
1002 initStyleOption(&opt, index);
1003 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
1004 QAbstractButton *closeButton =
new CloseButton(
this);
1005 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
1006 d, &QTabBarPrivate::closeTab);
1007 setTabButton(index, closeSide, closeButton);
1010 for (
const auto tab : std::as_const(d->tabList)) {
1011 if (tab->lastTab >= index)
1015 if (isVisible() && tabAt(d->mousePosition) == index) {
1016 if (d->normalizedScrollRect(index).contains(d->mousePosition)) {
1017 d->hoverIndex = index;
1018 d->hoverRect = tabRect(index);
1021 d->hoverRect = QRect();
1032
1033
1034
1035
1036void QTabBar::removeTab(
int index)
1039 if (d->validIndex(index)) {
1040 auto removedTab = d->tabList.at(index);
1041 if (d->dragInProgress)
1042 d->moveTabFinished(d->pressedIndex);
1044#ifndef QT_NO_SHORTCUT
1045 releaseShortcut(d->tabList.at(index)->shortcutId);
1047 if (removedTab->leftWidget) {
1048 removedTab->leftWidget->hide();
1049 removedTab->leftWidget->deleteLater();
1050 removedTab->leftWidget =
nullptr;
1052 if (removedTab->rightWidget) {
1053 removedTab->rightWidget->hide();
1054 removedTab->rightWidget->deleteLater();
1055 removedTab->rightWidget =
nullptr;
1058 int newIndex = removedTab->lastTab;
1059 d->tabList.removeAt(index);
1061 for (
auto tab : std::as_const(d->tabList)) {
1062 if (tab->lastTab == index)
1064 if (tab->lastTab > index)
1068 d->calculateFirstLastVisible(index,
false,
true);
1070 if (index == d->currentIndex) {
1074 d->currentIndex = -1;
1075 if (d->tabList.size() > 0) {
1076 switch(d->selectionBehaviorOnRemove) {
1077 case SelectPreviousTab:
1078 if (newIndex > index)
1080 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1083 case SelectRightTab:
1084 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1087 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1093 if (d->validIndex(newIndex)) {
1095 int bump = d->tabList.at(newIndex)->lastTab;
1096 setCurrentIndex(newIndex);
1097 d->tabList.at(newIndex)->lastTab = bump;
1100 emit currentChanged(-1);
1103 emit currentChanged(-1);
1105 }
else if (index < d->currentIndex) {
1106 setCurrentIndex(d->currentIndex - 1);
1110 if (d->hoverRect.isValid()) {
1111 update(d->hoverRect);
1112 d->hoverIndex = tabAt(d->mousePosition);
1113 if (d->validIndex(d->hoverIndex)
1114 && d->normalizedScrollRect(d->hoverIndex).contains(d->mousePosition)) {
1115 d->hoverRect = tabRect(d->hoverIndex);
1116 update(d->hoverRect);
1118 d->hoverRect = QRect();
1128
1129
1130
1131bool QTabBar::isTabEnabled(
int index)
const
1134 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1135 return tab->enabled;
1140
1141
1142
1143void QTabBar::setTabEnabled(
int index,
bool enabled)
1146 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1147 tab->enabled = enabled;
1148#ifndef QT_NO_SHORTCUT
1149 setShortcutEnabled(tab->shortcutId, enabled);
1152 if (!enabled && index == d->currentIndex)
1153 setCurrentIndex(d->selectNewCurrentIndexFrom(index+1));
1154 else if (enabled && !isTabVisible(d->currentIndex))
1155 setCurrentIndex(d->selectNewCurrentIndexFrom(index));
1161
1162
1163
1164
1165bool QTabBar::isTabVisible(
int index)
const
1168 if (d->validIndex(index))
1169 return d->tabList.at(index)->visible;
1174
1175
1176
1177
1178void QTabBar::setTabVisible(
int index,
bool visible)
1181 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1182 d->layoutDirty = (visible != tab->visible);
1183 if (!d->layoutDirty)
1185 tab->visible = visible;
1186 if (tab->leftWidget)
1187 tab->leftWidget->setVisible(visible);
1188 if (tab->rightWidget)
1189 tab->rightWidget->setVisible(visible);
1190#ifndef QT_NO_SHORTCUT
1191 setShortcutEnabled(tab->shortcutId, visible);
1193 d->calculateFirstLastVisible(index, visible,
false);
1194 if (!visible && index == d->currentIndex) {
1195 const int newindex = d->selectNewCurrentIndexFrom(index+1);
1196 setCurrentIndex(newindex);
1204
1205
1206
1207QString QTabBar::tabText(
int index)
const
1210 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1216
1217
1218void QTabBar::setTabText(
int index,
const QString &text)
1221 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1222 d->textSizes.remove(tab->text);
1224#ifndef QT_NO_SHORTCUT
1225 releaseShortcut(tab->shortcutId);
1226 tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1227 setShortcutEnabled(tab->shortcutId, tab->enabled);
1234
1235
1236
1237
1238
1239QColor QTabBar::tabTextColor(
int index)
const
1242 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1243 return tab->textColor;
1248
1249
1250
1251
1252
1253
1254void QTabBar::setTabTextColor(
int index,
const QColor &color)
1257 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1258 tab->textColor = color;
1259 update(tabRect(index));
1264
1265
1266
1267QIcon QTabBar::tabIcon(
int index)
const
1270 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1276
1277
1278void QTabBar::setTabIcon(
int index,
const QIcon & icon)
1281 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1282 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1284 if (simpleIconChange)
1285 update(tabRect(index));
1291#if QT_CONFIG(tooltip)
1293
1294
1295void QTabBar::setTabToolTip(
int index,
const QString & tip)
1298 if (QTabBarPrivate::Tab *tab = d->at(index))
1303
1304
1305
1306QString QTabBar::tabToolTip(
int index)
const
1309 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1310 return tab->toolTip;
1315#if QT_CONFIG(whatsthis)
1317
1318
1319
1320
1321
1322void QTabBar::setTabWhatsThis(
int index,
const QString &text)
1325 if (QTabBarPrivate::Tab *tab = d->at(index))
1326 tab->whatsThis = text;
1330
1331
1332
1333
1334
1335QString QTabBar::tabWhatsThis(
int index)
const
1338 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1339 return tab->whatsThis;
1346
1347
1348void QTabBar::setTabData(
int index,
const QVariant & data)
1351 if (QTabBarPrivate::Tab *tab = d->at(index))
1356
1357
1358
1359QVariant QTabBar::tabData(
int index)
const
1362 if (
const QTabBarPrivate::Tab *tab = d->at(index))
1368
1369
1370
1371QRect QTabBar::tabRect(
int index)
const
1374 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1376 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1379 QRect r = tab->rect;
1380 if (verticalTabs(d->shape))
1381 r.translate(0, -d->scrollOffset);
1383 r.translate(-d->scrollOffset, 0);
1384 if (!verticalTabs(d->shape))
1385 r = QStyle::visualRect(layoutDirection(), rect(), r);
1392
1393
1394
1395
1397int QTabBar::tabAt(
const QPoint &position)
const
1400 if (d->validIndex(d->currentIndex)
1401 && tabRect(d->currentIndex).contains(position)) {
1402 return d->currentIndex;
1404 const int max = d->tabList.size();
1405 for (
int i = 0; i < max; ++i) {
1406 if (tabRect(i).contains(position)) {
1414
1415
1416
1417
1418
1420int QTabBar::currentIndex()
const
1423 if (d->validIndex(d->currentIndex))
1424 return d->currentIndex;
1429void QTabBar::setCurrentIndex(
int index)
1432 if (d->dragInProgress && d->pressedIndex != -1)
1434 if (d->currentIndex == index)
1437 int oldIndex = d->currentIndex;
1438 if (
auto tab = d->at(index)) {
1439 d->currentIndex = index;
1445 if (tabRect(index).size() != tabSizeHint(index))
1449 d->layoutDirty =
true;
1451 d->makeVisible(index);
1452 if (d->validIndex(oldIndex)) {
1453 tab->lastTab = oldIndex;
1454 d->layoutTab(oldIndex);
1456 d->layoutTab(index);
1457#if QT_CONFIG(accessibility)
1458 if (QAccessible::isActive()) {
1460 QAccessibleEvent focusEvent(
this, QAccessible::Focus);
1461 focusEvent.setChild(index);
1462 QAccessible::updateAccessibility(&focusEvent);
1464 QAccessibleEvent selectionEvent(
this, QAccessible::Selection);
1465 selectionEvent.setChild(index);
1466 QAccessible::updateAccessibility(&selectionEvent);
1469 emit currentChanged(index);
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483QSize QTabBar::iconSize()
const
1486 if (d->iconSize.isValid())
1488 int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize,
nullptr,
this);
1489 return QSize(iconExtent, iconExtent);
1493void QTabBar::setIconSize(
const QSize &size)
1497 d->layoutDirty =
true;
1503
1504
1505
1507int QTabBar::count()
const
1510 return d->tabList.size();
1515
1516QSize QTabBar::sizeHint()
const
1520 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1522 for (
const auto tab : d->tabList) {
1524 r = r.united(tab->maxRect);
1530
1531QSize QTabBar::minimumSizeHint()
const
1535 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1536 if (!d->useScrollButtons) {
1538 for (
const auto tab : d->tabList) {
1540 r = r.united(tab->minRect);
1544 if (verticalTabs(d->shape))
1545 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1547 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1553 if (text.size() <= 3)
1556 static const auto Ellipses =
"..."_L1;
1559 case Qt::ElideRight:
1560 ret = QStringView{text}.left(2) + Ellipses;
1562 case Qt::ElideMiddle:
1563 ret = QStringView{text}.left(1) + Ellipses + QStringView{text}.right(1);
1566 ret = Ellipses + QStringView{text}.right(2);
1576
1577
1578
1580QSize QTabBar::minimumTabSizeHint(
int index)
const
1583 QTabBarPrivate::Tab *tab = d->tabList.at(index);
1584 QString oldText = tab->text;
1585 tab->text = computeElidedText(d->elideMode, oldText);
1586 tab->measuringMinimum =
true;
1587 QSize size = tabSizeHint(index);
1588 tab->text = oldText;
1589 tab->measuringMinimum =
false;
1594
1595
1596QSize QTabBar::tabSizeHint(
int index)
const
1600 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1601 QStyleOptionTab opt;
1602 d->initBasicStyleOption(&opt, index);
1603 opt.text = tab->text;
1604 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1605 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt,
this);
1606 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt,
this);
1607 const QFontMetrics fm = fontMetrics();
1609 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1610 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1612 int widgetWidth = 0;
1613 int widgetHeight = 0;
1615 if (!opt.leftButtonSize.isEmpty()) {
1617 widgetWidth += opt.leftButtonSize.width();
1618 widgetHeight += opt.leftButtonSize.height();
1620 if (!opt.rightButtonSize.isEmpty()) {
1622 widgetWidth += opt.rightButtonSize.width();
1623 widgetHeight += opt.rightButtonSize.height();
1625 if (!opt.icon.isNull())
1628 QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1629 if (it == d->textSizes.end())
1630 it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1631 const int textWidth = it.value().width();
1633 if (verticalTabs(d->shape)) {
1634 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1635 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1637 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1638 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1641 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1648
1649
1650
1651
1652
1653void QTabBar::tabInserted(
int index)
1659
1660
1661
1662
1663
1664void QTabBar::tabRemoved(
int index)
1670
1671
1672
1673
1674void QTabBar::tabLayoutChange()
1680
1681void QTabBar::showEvent(QShowEvent *)
1686 if (!d->validIndex(d->currentIndex))
1689 d->makeVisible(d->currentIndex);
1690 d->updateMacBorderMetrics();
1694
1695void QTabBar::hideEvent(QHideEvent *)
1698 d->updateMacBorderMetrics();
1702
1703bool QTabBar::event(QEvent *event)
1706 switch (event->type()) {
1707 case QEvent::HoverMove:
1708 case QEvent::HoverEnter: {
1709 QHoverEvent *he =
static_cast<QHoverEvent *>(event);
1710 d->mousePosition = he->position().toPoint();
1711 const auto sr = d->normalizedScrollRect();
1712 const auto oldHoverRect = d->hoverRect & sr;
1713 if (!oldHoverRect.contains(d->mousePosition)) {
1714 if (d->hoverRect.isValid())
1715 update(d->hoverRect);
1716 d->hoverIndex = tabAt(d->mousePosition);
1717 if (d->validIndex(d->hoverIndex) && sr.contains(d->mousePosition)) {
1718 d->hoverRect = tabRect(d->hoverIndex);
1719 update(d->hoverRect);
1721 d->hoverRect = QRect();
1727 case QEvent::HoverLeave: {
1728 d->mousePosition = {-1, -1};
1729 if (d->hoverRect.isValid())
1730 update(d->hoverRect);
1732 d->hoverRect = QRect();
1733#if QT_CONFIG(wheelevent)
1734 d->accumulatedAngleDelta = QPoint();
1738#if QT_CONFIG(tooltip)
1739 case QEvent::ToolTip:
1740 if (
const QTabBarPrivate::Tab *tab = d->at(tabAt(
static_cast<QHelpEvent*>(event)->pos()))) {
1741 if (!tab->toolTip.isEmpty()) {
1742 QToolTip::showText(
static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip,
this);
1748#if QT_CONFIG(whatsthis)
1749 case QEvent::QEvent::QueryWhatsThis: {
1750 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()));
1751 if (!tab || tab->whatsThis.isEmpty())
1755 case QEvent::WhatsThis:
1756 if (
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()))) {
1757 if (!tab->whatsThis.isEmpty()) {
1758 QWhatsThis::showText(
static_cast<QHelpEvent*>(event)->globalPos(),
1759 tab->whatsThis,
this);
1765#ifndef QT_NO_SHORTCUT
1767 case QEvent::Shortcut: {
1768 QShortcutEvent *se =
static_cast<QShortcutEvent *>(event);
1769 for (
int i = 0; i < d->tabList.size(); ++i) {
1770 const QTabBarPrivate::Tab *tab = d->tabList.at(i);
1771 if (tab->shortcutId == se->shortcutId()) {
1780 d->updateMacBorderMetrics();
1782#if QT_CONFIG(draganddrop)
1784 case QEvent::DragEnter:
1785 if (d->changeCurrentOnDrag)
1788 case QEvent::DragMove:
1789 if (d->changeCurrentOnDrag) {
1790 const int tabIndex = tabAt(
static_cast<QDragMoveEvent *>(event)->position().toPoint());
1791 if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1792 d->switchTabCurrentIndex = tabIndex;
1793 d->switchTabTimer.start(
1794 style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay,
nullptr,
this) * 1ms,
this);
1799 case QEvent::DragLeave:
1801 d->killSwitchTabTimer();
1805 case QEvent::MouseButtonPress:
1806 case QEvent::MouseButtonRelease:
1807 case QEvent::MouseMove:
1808 d->mousePosition =
static_cast<QMouseEvent *>(event)->position().toPoint();
1809 d->mouseButtons =
static_cast<QMouseEvent *>(event)->buttons();
1815 return QWidget::event(event);
1819
1820void QTabBar::resizeEvent(QResizeEvent *)
1829 d->makeVisible(d->currentIndex);
1833
1834void QTabBar::paintEvent(QPaintEvent *)
1838 QStyleOptionTabBarBase optTabBase;
1839 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1841 QStylePainter p(
this);
1845 bool vertical = verticalTabs(d->shape);
1846 QStyleOptionTab cutTabLeft;
1847 QStyleOptionTab cutTabRight;
1848 selected = d->currentIndex;
1849 if (d->dragInProgress)
1850 selected = d->pressedIndex;
1851 const QRect scrollRect = d->normalizedScrollRect();
1853 for (
int i = 0; i < d->tabList.size(); ++i)
1854 optTabBase.tabBarRect |= tabRect(i);
1856 optTabBase.selectedTabRect = tabRect(selected);
1859 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1863 if (d->leftB->isVisible() || d->rightB->isVisible()) {
1866 QRegion buttonRegion;
1867 if (d->leftB->isVisible()) {
1868 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt,
this);
1871 if (d->rightB->isVisible()) {
1872 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt,
this);
1875 if (!buttonRegion.isEmpty())
1876 p.setClipRegion(QRegion(rect()) - buttonRegion);
1879 for (
int i = 0; i < d->tabList.size(); ++i) {
1880 const auto tab = d->tabList.at(i);
1883 for (
const auto side : { QTabBar::LeftSide, QTabBar::RightSide }) {
1884 if (
auto closeButton = qobject_cast<CloseButton *>(tabButton(i, side)))
1885 closeButton->setParentClipRect(scrollRect);
1887 QStyleOptionTab tabOption;
1888 initStyleOption(&tabOption, i);
1889 if (tab->dragOffset != 0) {
1891 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1893 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1896 if (!(tabOption.state & QStyle::State_Enabled)) {
1897 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
1902 QRect tabRect = tab->rect;
1903 int tabStart = vertical ? tabRect.top() : tabRect.left();
1904 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1905 if (tabStart < scrollRect.left() + d->scrollOffset) {
1907 cutTabLeft = tabOption;
1908 }
else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1910 cutTabRight = tabOption;
1914 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1915 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1918 optTabBase.tabBarRect |= tabOption.rect;
1922 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1926 if (selected >= 0) {
1927 QStyleOptionTab tabOption;
1928 const auto tab = d->tabList.at(selected);
1929 initStyleOption(&tabOption, selected);
1931 if (tab->dragOffset != 0) {
1934 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1937 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1939 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1943 const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr,
this);
1944 const QRect &movingRect = verticalTabs(d->shape)
1945 ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
1946 : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
1950 if (d->dragInProgress)
1951 d->movingTab->setGeometry(movingRect);
1953 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1957 if (d->leftB->isVisible() && cutLeft >= 0) {
1958 cutTabLeft.rect = rect();
1959 cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft,
this);
1960 p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1963 if (d->rightB->isVisible() && cutRight >= 0) {
1964 cutTabRight.rect = rect();
1965 cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight,
this);
1966 p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
1971
1972
1973
1974void QTabBarPrivate::calculateFirstLastVisible(
int index,
bool visible,
bool remove)
1977 firstVisible = qMin(index, firstVisible);
1978 lastVisible = qMax(index, lastVisible);
1980 if (remove || (index == firstVisible)) {
1982 for (
int i = 0; i < tabList.size(); ++i) {
1983 if (tabList.at(i)->visible) {
1989 if (remove || (index == lastVisible)) {
1991 for (
int i = tabList.size() - 1; i >= 0; --i) {
1992 if (tabList.at(i)->visible) {
2002
2003
2004
2005
2006int QTabBarPrivate::selectNewCurrentIndexFrom(
int fromIndex)
2009 for (
int i = fromIndex; i < tabList.size(); ++i) {
2010 if (at(i)->visible && at(i)->enabled) {
2016 for (
int i = fromIndex-1; i > -1; --i) {
2017 if (at(i)->visible && at(i)->enabled) {
2028
2029
2030int QTabBarPrivate::calculateNewPosition(
int from,
int to,
int index)
const
2035 int start = qMin(from, to);
2036 int end = qMax(from, to);
2037 if (index >= start && index <= end)
2038 index += (from < to) ? -1 : 1;
2043
2044
2045
2046
2047
2048void QTabBar::moveTab(
int from,
int to)
2052 || !d->validIndex(from)
2053 || !d->validIndex(to))
2056 auto &fromTab = *d->tabList.at(from);
2057 auto &toTab = *d->tabList.at(to);
2059 bool vertical = verticalTabs(d->shape);
2060 int oldPressedPosition = 0;
2061 if (d->pressedIndex != -1) {
2063 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2064 : d->tabList.at(d->pressedIndex)->rect.x();
2068 int start = qMin(from, to);
2069 int end = qMax(from, to);
2070 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2073 bool rtl = isRightToLeft();
2074 for (
int i = start; i <= end; ++i) {
2077 auto &tab = *d->tabList.at(i);
2079 tab.rect.moveTop(tab.rect.y() + width);
2081 tab.rect.moveLeft(tab.rect.x() + width);
2083 if (rtl && !vertical)
2085 if (tab.dragOffset != 0)
2086 tab.dragOffset += (direction * width);
2091 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2093 fromTab.rect.moveTop(toTab.rect.top() - width);
2096 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2098 fromTab.rect.moveLeft(toTab.rect.left() - width);
2102 d->tabList.move(from, to);
2105 for (
const auto tab : std::as_const(d->tabList))
2106 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2109 int previousIndex = d->currentIndex;
2110 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2113 if (d->pressedIndex != -1) {
2114 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2115 const auto pressedTab = d->tabList.at(d->pressedIndex);
2116 int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
2117 int diff = oldPressedPosition - newPressedPosition;
2118 if (isRightToLeft() && !vertical)
2121 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2123 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2126 d->layoutWidgets(start);
2128 emit tabMoved(from, to);
2129 if (previousIndex != d->currentIndex)
2130 emit currentChanged(d->currentIndex);
2131 emit tabLayoutChange();
2134void QTabBarPrivate::slide(
int from,
int to)
2138 || !validIndex(from)
2141 bool vertical = verticalTabs(shape);
2142 int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
2143 q->setUpdatesEnabled(
false);
2144 q->moveTab(from, to);
2145 q->setUpdatesEnabled(
true);
2146 int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
2147 int length = postLocation - preLocation;
2148 tabList.at(to)->dragOffset -= length;
2149 tabList.at(to)->startAnimation(
this, ANIMATION_DURATION);
2152void QTabBarPrivate::moveTab(
int index,
int offset)
2154 if (!validIndex(index))
2156 tabList.at(index)->dragOffset = offset;
2162
2163void QTabBar::mousePressEvent(QMouseEvent *event)
2167 const QPoint pos = event->position().toPoint();
2168 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2169 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2170 if (!isEventInCornerButtons) {
2171 const int index = d->indexAtPos(pos);
2172 emit tabBarClicked(index);
2175 if (event->button() != Qt::LeftButton) {
2180 if (d->pressedIndex != -1 && d->movable)
2181 d->moveTabFinished(d->pressedIndex);
2183 d->pressedIndex = d->indexAtPos(event->position().toPoint());
2185 if (d->validIndex(d->pressedIndex)) {
2186 QStyleOptionTabBarBase optTabBase;
2187 optTabBase.initFrom(
this);
2188 optTabBase.documentMode = d->documentMode;
2189 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this))
2190 setCurrentIndex(d->pressedIndex);
2192 repaint(tabRect(d->pressedIndex));
2194 d->dragStartPosition = event->position().toPoint();
2200
2201void QTabBar::mouseMoveEvent(QMouseEvent *event)
2206 if (d->pressedIndex != -1
2207 && event->buttons() == Qt::NoButton)
2208 d->moveTabFinished(d->pressedIndex);
2211 if (!d->dragInProgress && d->pressedIndex != -1) {
2212 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2213 d->dragInProgress =
true;
2214 d->setupMovableTab();
2218 if (event->buttons() == Qt::LeftButton
2219 && d->dragInProgress
2220 && d->validIndex(d->pressedIndex)) {
2221 bool vertical = verticalTabs(d->shape);
2224 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2226 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2228 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2230 QRect startingRect = tabRect(d->pressedIndex);
2232 startingRect.moveTop(startingRect.y() + dragDistance);
2234 startingRect.moveLeft(startingRect.x() + dragDistance);
2237 if (dragDistance < 0)
2238 overIndex = tabAt(startingRect.topLeft());
2240 overIndex = tabAt(startingRect.topRight());
2242 if (overIndex != d->pressedIndex && overIndex != -1) {
2244 if (isRightToLeft() && !vertical)
2246 if (dragDistance < 0) {
2250 for (
int i = d->pressedIndex;
2251 offset > 0 ? i < overIndex : i > overIndex;
2253 QRect overIndexRect = tabRect(overIndex);
2254 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2255 if (dragDistance > needsToBeOver)
2256 d->slide(i + offset, d->pressedIndex);
2260 if (d->pressedIndex != -1)
2261 d->layoutTab(d->pressedIndex);
2267 if (event->buttons() != Qt::LeftButton) {
2273void QTabBarPrivate::setupMovableTab()
2277 movingTab =
new QMovableTabWidget(q);
2279 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr ,q);
2280 QRect grabRect = q->tabRect(pressedIndex);
2281 if (verticalTabs(shape))
2282 grabRect.adjust(0, -taboverlap, 0, taboverlap);
2284 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2286 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2287 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2288 grabImage.fill(Qt::transparent);
2289 QStylePainter p(&grabImage, q);
2291 QStyleOptionTab tab;
2292 q->initStyleOption(&tab, pressedIndex);
2293 tab.position = QStyleOptionTab::Moving;
2294 if (verticalTabs(shape))
2295 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2297 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2298 p.drawControl(QStyle::CE_TabBarTab, tab);
2301 movingTab->setPixmap(grabImage);
2302 movingTab->setGeometry(grabRect);
2306 const auto &pressedTab = *tabList.at(pressedIndex);
2307 if (pressedTab.leftWidget)
2308 pressedTab.leftWidget->raise();
2309 if (pressedTab.rightWidget)
2310 pressedTab.rightWidget->raise();
2315 movingTab->setVisible(
true);
2318void QTabBarPrivate::moveTabFinished(
int index)
2321 bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
2322 bool allAnimationsFinished =
true;
2323#if QT_CONFIG(animation)
2324 for (
const auto tab : std::as_const(tabList)) {
2325 if (tab->animation && tab->animation->state() == QAbstractAnimation::Running) {
2326 allAnimationsFinished =
false;
2331 if (allAnimationsFinished && cleanup) {
2333 movingTab->setVisible(
false);
2334 for (
auto tab : std::as_const(tabList)) {
2335 tab->dragOffset = 0;
2337 if (pressedIndex != -1 && movable) {
2339 dragInProgress =
false;
2340 dragStartPosition = QPoint();
2344 if (!validIndex(index))
2346 tabList.at(index)->dragOffset = 0;
2352
2353void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2357 if (d->closeButtonOnTabs && event->button() == Qt::MiddleButton) {
2358 const int index = tabAt(event->pos());
2360 emit tabCloseRequested(index);
2365 if (event->button() != Qt::LeftButton) {
2370 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2371 int length = d->tabList.at(d->pressedIndex)->dragOffset;
2372 int width = verticalTabs(d->shape)
2373 ? tabRect(d->pressedIndex).height()
2374 : tabRect(d->pressedIndex).width();
2375 int duration = qMin(ANIMATION_DURATION,
2376 (qAbs(length) * ANIMATION_DURATION) / width);
2377 d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
2378 d->dragInProgress =
false;
2379 d->movingTab->setVisible(
false);
2380 d->dragStartPosition = QPoint();
2384 int oldPressedIndex = d->pressedIndex;
2385 int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
2386 d->pressedIndex = -1;
2387 QStyleOptionTabBarBase optTabBase;
2388 optTabBase.initFrom(
this);
2389 optTabBase.documentMode = d->documentMode;
2390 const bool selectOnRelease =
2391 (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this) == QEvent::MouseButtonRelease);
2392 if (selectOnRelease)
2394 if (d->validIndex(oldPressedIndex))
2395 update(tabRect(oldPressedIndex));
2399
2400void QTabBar::mouseDoubleClickEvent(QMouseEvent *event)
2403 const QPoint pos = event->position().toPoint();
2404 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2405 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2406 if (!isEventInCornerButtons)
2407 emit tabBarDoubleClicked(tabAt(pos));
2409 mousePressEvent(event);
2413
2414void QTabBar::keyPressEvent(QKeyEvent *event)
2417 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
2421 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
2422 d->setCurrentNextEnabledIndex(offset);
2426
2427#if QT_CONFIG(wheelevent)
2428void QTabBar::wheelEvent(QWheelEvent *event)
2431 if (style()->styleHint(QStyle::SH_TabBar_AllowWheelScrolling,
nullptr,
this)) {
2432 const bool wheelVertical = qAbs(event->angleDelta().y()) > qAbs(event->angleDelta().x());
2433 const bool tabsVertical = verticalTabs(d->shape);
2434 if (event->device()->capabilities().testFlag(QInputDevice::Capability::PixelScroll)) {
2438 if (tabsVertical == wheelVertical)
2439 delta = wheelVertical ? event->pixelDelta().y() : event->pixelDelta().x();
2440 if (layoutDirection() == Qt::RightToLeft)
2442 if (delta && d->validIndex(d->lastVisible)) {
2443 const int oldScrollOffset = d->scrollOffset;
2444 const QRect lastTabRect = d->tabList.at(d->lastVisible)->rect;
2445 const QRect scrollRect = d->normalizedScrollRect(d->lastVisible);
2446 int scrollRectExtent = scrollRect.right();
2447 if (!d->leftB->isVisible())
2448 scrollRectExtent += tabsVertical ? d->leftB->height() : d->leftB->width();
2449 if (!d->rightB->isVisible())
2450 scrollRectExtent += tabsVertical ? d->rightB->height() : d->rightB->width();
2452 const QRect scrollRect0 = d->normalizedScrollRect(0);
2453 const int minScrollOffset = -1 * scrollRect0.left();
2454 const int maxScrollOffset = qMax((tabsVertical ?
2455 lastTabRect.bottom() :
2456 lastTabRect.right()) - scrollRectExtent, 0);
2457 d->scrollOffset = qBound(minScrollOffset, d->scrollOffset - delta, maxScrollOffset);
2458 d->leftB->setEnabled(d->scrollOffset > -scrollRect.left());
2459 d->rightB->setEnabled(maxScrollOffset > d->scrollOffset);
2460 if (oldScrollOffset != d->scrollOffset) {
2468 d->accumulatedAngleDelta += event->angleDelta();
2469 const int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
2470 const int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
2472 if (xSteps > 0 || ySteps > 0) {
2474 d->accumulatedAngleDelta = QPoint();
2475 }
else if (xSteps < 0 || ySteps < 0) {
2477 d->accumulatedAngleDelta = QPoint();
2479 const int oldCurrentIndex = d->currentIndex;
2480 d->setCurrentNextEnabledIndex(offset);
2481 if (oldCurrentIndex != d->currentIndex) {
2486 QWidget::wheelEvent(event);
2491void QTabBarPrivate::setCurrentNextEnabledIndex(
int offset)
2494 for (
int index = currentIndex + offset; validIndex(index); index += offset) {
2495 if (tabList.at(index)->enabled && tabList.at(index)->visible) {
2496 q->setCurrentIndex(index);
2503
2504void QTabBar::changeEvent(QEvent *event)
2507 switch (event->type()) {
2508 case QEvent::StyleChange:
2509 if (!d->elideModeSetByUser)
2510 d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode,
nullptr,
this));
2511 if (!d->useScrollButtonsSetByUser)
2512 d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows,
nullptr,
this);
2514 case QEvent::FontChange:
2515 d->textSizes.clear();
2522 QWidget::changeEvent(event);
2526
2527
2528void QTabBar::timerEvent(QTimerEvent *event)
2531 if (event->id() == d->switchTabTimer.id()) {
2532 d->switchTabTimer.stop();
2533 setCurrentIndex(d->switchTabCurrentIndex);
2534 d->switchTabCurrentIndex = -1;
2536 QWidget::timerEvent(event);
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2552Qt::TextElideMode QTabBar::elideMode()
const
2555 return d->elideMode;
2558void QTabBar::setElideMode(Qt::TextElideMode mode)
2561 d->elideMode = mode;
2562 d->elideModeSetByUser =
true;
2563 d->textSizes.clear();
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580bool QTabBar::usesScrollButtons()
const
2582 return d_func()->useScrollButtons;
2585void QTabBar::setUsesScrollButtons(
bool useButtons)
2588 d->useScrollButtonsSetByUser =
true;
2589 if (d->useScrollButtons == useButtons)
2591 d->useScrollButtons = useButtons;
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2610bool QTabBar::tabsClosable()
const
2613 return d->closeButtonOnTabs;
2616void QTabBar::setTabsClosable(
bool closable)
2619 if (d->closeButtonOnTabs == closable)
2621 d->closeButtonOnTabs = closable;
2622 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
2624 for (
auto tab : std::as_const(d->tabList)) {
2625 if (closeSide == LeftSide && tab->leftWidget) {
2626 tab->leftWidget->deleteLater();
2627 tab->leftWidget =
nullptr;
2629 if (closeSide == RightSide && tab->rightWidget) {
2630 tab->rightWidget->deleteLater();
2631 tab->rightWidget =
nullptr;
2635 bool newButtons =
false;
2636 for (
int i = 0; i < d->tabList.size(); ++i) {
2637 if (tabButton(i, closeSide))
2640 QAbstractButton *closeButton =
new CloseButton(
this);
2641 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
2642 d, &QTabBarPrivate::closeTab);
2643 setTabButton(i, closeSide, closeButton);
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2679
2680
2681
2682
2683
2684
2685
2686
2687
2690QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove()
const
2693 return d->selectionBehaviorOnRemove;
2696void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2699 d->selectionBehaviorOnRemove = behavior;
2703
2704
2705
2706
2707
2708
2709
2710
2712bool QTabBar::expanding()
const
2715 return d->expanding;
2718void QTabBar::setExpanding(
bool enabled)
2721 if (d->expanding == enabled)
2723 d->expanding = enabled;
2728
2729
2730
2731
2732
2733
2734
2735
2737bool QTabBar::isMovable()
const
2743void QTabBar::setMovable(
bool movable)
2746 d->movable = movable;
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761bool QTabBar::documentMode()
const
2763 return d_func()->documentMode;
2766void QTabBar::setDocumentMode(
bool enabled)
2770 d->documentMode = enabled;
2771 d->updateMacBorderMetrics();
2775
2776
2777
2778
2779
2780
2781
2782
2783
2785bool QTabBar::autoHide()
const
2791void QTabBar::setAutoHide(
bool hide)
2794 if (d->autoHide == hide)
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2816bool QTabBar::changeCurrentOnDrag()
const
2819 return d->changeCurrentOnDrag;
2822void QTabBar::setChangeCurrentOnDrag(
bool change)
2825 d->changeCurrentOnDrag = change;
2827 d->killSwitchTabTimer();
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844void QTabBar::setTabButton(
int index, ButtonPosition position, QWidget *widget)
2847 if (index < 0 || index >= d->tabList.size())
2850 widget->setParent(
this);
2855 auto &tab = *d->tabList.at(index);
2856 if (position == LeftSide) {
2858 tab.leftWidget->hide();
2859 tab.leftWidget = widget;
2861 if (tab.rightWidget)
2862 tab.rightWidget->hide();
2863 tab.rightWidget = widget;
2871
2872
2873
2874QWidget *QTabBar::tabButton(
int index, ButtonPosition position)
const
2877 if (
const auto tab = d->at(index)) {
2878 return position == LeftSide ? tab->leftWidget
2884#if QT_CONFIG(accessibility)
2886
2887
2888void QTabBar::setAccessibleTabName(
int index,
const QString &name)
2891 if (QTabBarPrivate::Tab *tab = d->at(index)) {
2892 tab->accessibleName = name;
2893 QAccessibleEvent event(
this, QAccessible::NameChanged);
2894 event.setChild(index);
2895 QAccessible::updateAccessibility(&event);
2900
2901
2902
2903QString QTabBar::accessibleTabName(
int index)
const
2906 if (
const QTabBarPrivate::Tab *tab = d->at(index))
2907 return tab->accessibleName;
2912CloseButton::CloseButton(
QWidget *parent)
2913 : QAbstractButton(parent)
2915 setFocusPolicy(Qt::NoFocus);
2917 setCursor(Qt::ArrowCursor);
2919#if QT_CONFIG(tooltip)
2920 setToolTip(tr(
"Close Tab"));
2925QSize CloseButton::sizeHint()
const
2928 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth,
nullptr,
this);
2929 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight,
nullptr,
this);
2930 return QSize(width, height);
2933void CloseButton::enterEvent(QEnterEvent *event)
2937 QAbstractButton::enterEvent(event);
2940void CloseButton::leaveEvent(QEvent *event)
2944 QAbstractButton::leaveEvent(event);
2947void CloseButton::paintEvent(QPaintEvent *)
2952 opt.state |= QStyle::State_AutoRaise;
2953 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2954 opt.state |= QStyle::State_Raised;
2956 opt.state |= QStyle::State_On;
2958 opt.state |= QStyle::State_Sunken;
2960 if (
const QTabBar *tb = qobject_cast<
const QTabBar *>(parent())) {
2961 int index = tb->currentIndex();
2962 QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr, tb);
2963 if (tb->tabButton(index, position) ==
this)
2964 opt.state |= QStyle::State_Selected;
2967 if (m_parentClipRect.isValid()) {
2968 auto tl = mapFromParent(m_parentClipRect.topLeft());
2969 auto br = mapFromParent(m_parentClipRect.bottomRight());
2970 p.setClipRect(QRect(tl, br));
2972 style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p,
this);
2975#if QT_CONFIG(animation)
2976void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(
const QVariant ¤t)
2978 priv->moveTab(priv->tabList.indexOf(tab), current.toInt());
2981void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
2983 if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(tab));
2989#include "moc_qtabbar.cpp"
2990#include "qtabbar.moc"
Combined button and popup list for selecting options.
static QString computeElidedText(Qt::TextElideMode mode, const QString &text)