127void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option,
int tabIndex)
const
130 const int totalTabs = tabList.size();
132 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
135 const QTabBarPrivate::Tab &tab = *tabList.at(tabIndex);
137 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
138 option->rect = q->tabRect(tabIndex);
139 const bool isCurrent = tabIndex == currentIndex;
141 if (tabIndex == pressedIndex)
142 option->state |= QStyle::State_Sunken;
144 option->state |= QStyle::State_Selected;
145 if (isCurrent && q->hasFocus())
146 option->state |= QStyle::State_HasFocus;
148 option->state &= ~QStyle::State_Enabled;
149 if (q->isActiveWindow())
150 option->state |= QStyle::State_Active;
151 if (!dragInProgress && option->rect == hoverRect)
152 option->state |= QStyle::State_MouseOver;
153 option->shape = shape;
154 option->text = tab.text;
156 if (tab.textColor.isValid())
157 option->palette.setColor(q->foregroundRole(), tab.textColor);
158 option->icon = tab.icon;
159 option->iconSize = q->iconSize();
161 option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
162 option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
163 option->documentMode = documentMode;
165 if (tabIndex > 0 && tabIndex - 1 == currentIndex)
166 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
167 else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
168 option->selectedPosition = QStyleOptionTab::NextIsSelected;
170 option->selectedPosition = QStyleOptionTab::NotAdjacent;
172 const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
173 const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
174 if (paintBeginning) {
176 option->position = QStyleOptionTab::OnlyOneTab;
178 option->position = QStyleOptionTab::Beginning;
179 }
else if (paintEnd) {
180 option->position = QStyleOptionTab::End;
182 option->position = QStyleOptionTab::Middle;
185#if QT_CONFIG(tabwidget)
186 if (
const QTabWidget *tw = qobject_cast<
const QTabWidget *>(q->parentWidget())) {
187 option->features |= QStyleOptionTab::HasFrame;
188 if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
189 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
190 if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
191 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
194 if (tab.measuringMinimum)
195 option->features |= QStyleOptionTab::MinimumSizeHint;
196 option->tabIndex = tabIndex;
367void QTabBarPrivate::init()
370 leftB =
new QToolButton(q);
371 leftB->setObjectName(u"ScrollLeftButton"_s);
372 leftB->setAutoRepeat(
true);
373 QObjectPrivate::connect(leftB, &QToolButton::clicked,
374 this, &QTabBarPrivate::scrollTabs);
376 rightB =
new QToolButton(q);
377 rightB->setObjectName(u"ScrollRightButton"_s);
378 rightB->setAutoRepeat(
true);
379 QObjectPrivate::connect(rightB, &QToolButton::clicked,
380 this, &QTabBarPrivate::scrollTabs);
382#ifdef QT_KEYPAD_NAVIGATION
383 if (QApplicationPrivate::keypadNavigationEnabled()) {
384 leftB->setFocusPolicy(Qt::NoFocus);
385 rightB->setFocusPolicy(Qt::NoFocus);
386 q->setFocusPolicy(Qt::NoFocus);
389 q->setFocusPolicy(Qt::TabFocus);
391#if QT_CONFIG(accessibility)
392 leftB->setAccessibleName(QTabBar::tr(
"Scroll Left"));
393 rightB->setAccessibleName(QTabBar::tr(
"Scroll Right"));
395 q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
396 elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode,
nullptr, q));
397 useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows,
nullptr, q);
411void QTabBarPrivate::layoutTabs()
415 QSize size = q->size();
418 bool vertTabs = verticalTabs(shape);
419 int tabChainIndex = 0;
421 const auto *style = q->style();
423 Qt::Alignment tabAlignment = Qt::Alignment(style->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
424 QList<QLayoutStruct> tabChain(tabList.size() + 2);
428 tabChain[tabChainIndex].init();
429 tabChain[tabChainIndex].expansive = (!expanding)
430 && (tabAlignment != Qt::AlignLeft)
431 && (tabAlignment != Qt::AlignJustify);
432 tabChain[tabChainIndex].empty =
true;
447 for (
int i = 0; i < tabList.size(); ++i) {
448 const auto tab = tabList.at(i);
453 QSize sz = q->tabSizeHint(i);
454 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
456 maxHeight = qMax(maxHeight, sz.height());
457 sz = q->minimumTabSizeHint(i);
458 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
460 tabChain[tabChainIndex].init();
461 tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
462 tabChain[tabChainIndex].minimumSize = sz.width();
463 tabChain[tabChainIndex].empty =
false;
464 tabChain[tabChainIndex].expansive =
true;
467 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
472 available = size.width();
473 maxExtent = maxHeight;
478 for (
int i = 0; i < tabList.size(); ++i) {
479 auto tab = tabList.at(i);
484 QSize sz = q->tabSizeHint(i);
485 tab->maxRect = QRect(0, y, sz.width(), sz.height());
487 maxWidth = qMax(maxWidth, sz.width());
488 sz = q->minimumTabSizeHint(i);
489 tab->minRect = QRect(0, miny, sz.width(), sz.height());
491 tabChain[tabChainIndex].init();
492 tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
493 tabChain[tabChainIndex].minimumSize = sz.height();
494 tabChain[tabChainIndex].empty =
false;
495 tabChain[tabChainIndex].expansive =
true;
498 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
503 available = size.height();
504 maxExtent = maxWidth;
508 tabChain[tabChainIndex].init();
509 tabChain[tabChainIndex].expansive = (!expanding)
510 && (tabAlignment != Qt::AlignRight)
511 && (tabAlignment != Qt::AlignJustify);
512 tabChain[tabChainIndex].empty =
true;
513 Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs);
516 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
520 for (
int i = 0; i < tabList.size(); ++i) {
521 auto tab = tabList.at(i);
527 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
529 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
531 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
534 if (useScrollButtons && tabList.size() && last > available) {
535 const QRect scrollRect = normalizedScrollRect(0);
540 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
541 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
542 int scrollButtonWidth = style->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
544 const auto setIconOr = [](QToolButton *tb,
const QIcon &icon, Qt::ArrowType arrowType) {
546 tb->setArrowType(arrowType);
554 scrollButtonLeftRect.setHeight(scrollButtonWidth);
555 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
556 scrollButtonRightRect.setHeight(scrollButtonWidth);
557 const auto upIcon = style->standardIcon(QStyle::SP_TabScrollUpButton, &opt, q);
558 const auto downIcon = style->standardIcon(QStyle::SP_TabScrollDownButton, &opt, q);
559 setIconOr(leftB, upIcon, Qt::UpArrow);
560 setIconOr(rightB, downIcon, Qt::DownArrow);
561 }
else if (q->layoutDirection() == Qt::RightToLeft) {
562 scrollButtonRightRect.setWidth(scrollButtonWidth);
563 scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
564 scrollButtonLeftRect.setWidth(scrollButtonWidth);
565 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
566 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
567 setIconOr(leftB, rightIcon, Qt::RightArrow);
568 setIconOr(rightB, leftIcon, Qt::LeftArrow);
570 scrollButtonLeftRect.setWidth(scrollButtonWidth);
571 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
572 scrollButtonRightRect.setWidth(scrollButtonWidth);
573 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
574 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
575 setIconOr(leftB, leftIcon, Qt::LeftArrow);
576 setIconOr(rightB, rightIcon, Qt::RightArrow);
579 leftB->setGeometry(scrollButtonLeftRect);
580 leftB->setEnabled(
false);
583 rightB->setGeometry(scrollButtonRightRect);
584 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
592 q->tabLayoutChange();
601QRect QTabBarPrivate::normalizedScrollRect(
int index)
const
611 if (leftB->isHidden())
612 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
615 q->initStyleOption(&opt, currentIndex);
616 opt.rect = q->rect();
618 const auto style = q->style();
619 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
620 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
621 QRect tearLeftRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
622 QRect tearRightRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
624 if (verticalTabs(shape)) {
625 int topEdge, bottomEdge;
626 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
627 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
629 if (leftButtonIsOnTop && rightButtonIsOnTop) {
630 topEdge = scrollButtonRightRect.bottom() + 1;
631 bottomEdge = q->height();
632 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
634 bottomEdge = scrollButtonLeftRect.top();
636 topEdge = scrollButtonLeftRect.bottom() + 1;
637 bottomEdge = scrollButtonRightRect.top();
640 const auto lastTab = lastVisibleTab();
643 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
644 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != lastTab->rect.bottom() + 1 - scrollOffset;
645 if (tearTopVisible && !tearLeftRect.isNull())
646 topEdge = tearLeftRect.bottom() + 1;
647 if (tearBottomVisible && !tearRightRect.isNull())
648 bottomEdge = tearRightRect.top();
650 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
652 if (q->layoutDirection() == Qt::RightToLeft) {
653 scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
654 scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
655 tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
656 tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
659 int leftEdge, rightEdge;
660 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
661 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
663 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
664 leftEdge = scrollButtonRightRect.right() + 1;
665 rightEdge = q->width();
666 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
668 rightEdge = scrollButtonLeftRect.left();
670 leftEdge = scrollButtonLeftRect.right() + 1;
671 rightEdge = scrollButtonRightRect.left();
674 const auto lastTab = lastVisibleTab();
677 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
678 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != lastTab->rect.right() + 1 - scrollOffset;
679 if (tearLeftVisible && !tearLeftRect.isNull())
680 leftEdge = tearLeftRect.right() + 1;
681 if (tearRightVisible && !tearRightRect.isNull())
682 rightEdge = tearRightRect.left();
684 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
697void QTabBarPrivate::makeVisible(
int index)
700 if (!validIndex(index))
703 const auto lastTab = lastVisibleTab();
704 const QRect tabRect = tabList.at(index)->rect;
705 const int oldScrollOffset = scrollOffset;
706 const bool horiz = !verticalTabs(shape);
707 const int available = horiz ? q->width() : q->height();
708 const int tabStart = horiz ? tabRect.left() : tabRect.top();
709 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
710 const int lastTabEnd = lastTab ? (horiz ? lastTab->rect.right() : lastTab->rect.bottom()) : 0;
711 const QRect scrollRect = normalizedScrollRect(index);
712 const QRect entireScrollRect = normalizedScrollRect(0);
713 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
714 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
716 if (available >= lastTabEnd) {
719 }
else if (tabStart < scrolledTabBarStart) {
721 scrollOffset = tabStart - scrollRect.left();
722 }
else if (tabEnd > scrolledTabBarEnd) {
724 scrollOffset = qMax(0, tabEnd - scrollRect.right());
725 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
727 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
730 leftB->setEnabled(scrollOffset > -scrollRect.left());
731 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
733 if (oldScrollOffset != scrollOffset) {
985int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
988 if (!d->validIndex(index)) {
989 index = d->tabList.size();
990 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
992 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
994#ifndef QT_NO_SHORTCUT
995 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
997 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
999 if (d->tabList.size() == 1)
1000 setCurrentIndex(index);
1001 else if (index <= d->currentIndex)
1004 if (index <= d->lastVisible)
1007 d->lastVisible = index;
1009 if (d->closeButtonOnTabs) {
1010 QStyleOptionTab opt;
1011 initStyleOption(&opt, index);
1012 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
1013 QAbstractButton *closeButton =
new CloseButton(
this);
1014 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
1015 d, &QTabBarPrivate::closeTab);
1016 setTabButton(index, closeSide, closeButton);
1019 for (
const auto tab : std::as_const(d->tabList)) {
1020 if (tab->lastTab >= index)
1024 if (isVisible() && tabAt(d->mousePosition) == index) {
1025 if (d->scrollRect(index).contains(d->mousePosition)) {
1026 d->hoverIndex = index;
1027 d->hoverRect = tabRect(index);
1030 d->hoverRect = QRect();
1045void QTabBar::removeTab(
int index)
1048 if (d->validIndex(index)) {
1049 auto removedTab = d->tabList.at(index);
1050 if (d->dragInProgress)
1051 d->moveTabFinished(d->pressedIndex);
1053#ifndef QT_NO_SHORTCUT
1054 releaseShortcut(d->tabList.at(index)->shortcutId);
1056 if (removedTab->leftWidget) {
1057 removedTab->leftWidget->hide();
1058 removedTab->leftWidget->deleteLater();
1059 removedTab->leftWidget =
nullptr;
1061 if (removedTab->rightWidget) {
1062 removedTab->rightWidget->hide();
1063 removedTab->rightWidget->deleteLater();
1064 removedTab->rightWidget =
nullptr;
1067 int newIndex = removedTab->lastTab;
1068 d->tabList.removeAt(index);
1070 for (
auto tab : std::as_const(d->tabList)) {
1071 if (tab->lastTab == index)
1073 if (tab->lastTab > index)
1077 d->calculateFirstLastVisible(index,
false,
true);
1079 if (index == d->currentIndex) {
1083 d->currentIndex = -1;
1084 if (d->tabList.size() > 0) {
1085 switch(d->selectionBehaviorOnRemove) {
1086 case SelectPreviousTab:
1087 if (newIndex > index)
1089 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1092 case SelectRightTab:
1093 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1096 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1102 if (d->validIndex(newIndex)) {
1104 int bump = d->tabList.at(newIndex)->lastTab;
1105 setCurrentIndex(newIndex);
1106 d->tabList.at(newIndex)->lastTab = bump;
1109 emit currentChanged(-1);
1112 emit currentChanged(-1);
1114 }
else if (index < d->currentIndex) {
1115 setCurrentIndex(d->currentIndex - 1);
1119 if (d->hoverRect.isValid()) {
1120 update(d->hoverRect);
1121 d->hoverIndex = tabAt(d->mousePosition);
1122 if (d->validIndex(d->hoverIndex)
1123 && d->scrollRect(d->hoverIndex).contains(d->mousePosition)) {
1124 d->hoverRect = tabRect(d->hoverIndex);
1125 update(d->hoverRect);
1127 d->hoverRect = QRect();
1605QSize QTabBar::tabSizeHint(
int index)
const
1609 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1610 QStyleOptionTab opt;
1611 d->initBasicStyleOption(&opt, index);
1612 opt.text = tab->text;
1613 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1614 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt,
this);
1615 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt,
this);
1616 const QFontMetrics fm = fontMetrics();
1618 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1619 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1621 int widgetWidth = 0;
1622 int widgetHeight = 0;
1624 if (!opt.leftButtonSize.isEmpty()) {
1626 widgetWidth += opt.leftButtonSize.width();
1627 widgetHeight += opt.leftButtonSize.height();
1629 if (!opt.rightButtonSize.isEmpty()) {
1631 widgetWidth += opt.rightButtonSize.width();
1632 widgetHeight += opt.rightButtonSize.height();
1634 if (!opt.icon.isNull())
1637 QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1638 if (it == d->textSizes.end())
1639 it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1640 const int textWidth = it.value().width();
1642 if (verticalTabs(d->shape)) {
1643 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1644 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1646 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1647 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1650 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1712bool QTabBar::event(QEvent *event)
1715 switch (event->type()) {
1716 case QEvent::HoverMove:
1717 case QEvent::HoverEnter: {
1718 QHoverEvent *he =
static_cast<QHoverEvent *>(event);
1719 d->mousePosition = he->position().toPoint();
1720 const auto sr = d->scrollRect();
1721 const auto oldHoverRect = d->hoverRect & sr;
1722 if (!oldHoverRect.contains(d->mousePosition)) {
1723 if (d->hoverRect.isValid())
1724 update(d->hoverRect);
1725 d->hoverIndex = tabAt(d->mousePosition);
1726 if (d->validIndex(d->hoverIndex) && sr.contains(d->mousePosition)) {
1727 d->hoverRect = tabRect(d->hoverIndex);
1728 update(d->hoverRect);
1730 d->hoverRect = QRect();
1736 case QEvent::HoverLeave: {
1737 d->mousePosition = {-1, -1};
1738 if (d->hoverRect.isValid())
1739 update(d->hoverRect);
1741 d->hoverRect = QRect();
1742#if QT_CONFIG(wheelevent)
1743 d->accumulatedAngleDelta = QPoint();
1747#if QT_CONFIG(tooltip)
1748 case QEvent::ToolTip:
1749 if (
const QTabBarPrivate::Tab *tab = d->at(tabAt(
static_cast<QHelpEvent*>(event)->pos()))) {
1750 if (!tab->toolTip.isEmpty()) {
1751 QToolTip::showText(
static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip,
this);
1757#if QT_CONFIG(whatsthis)
1758 case QEvent::QEvent::QueryWhatsThis: {
1759 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()));
1760 if (!tab || tab->whatsThis.isEmpty())
1764 case QEvent::WhatsThis:
1765 if (
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()))) {
1766 if (!tab->whatsThis.isEmpty()) {
1767 QWhatsThis::showText(
static_cast<QHelpEvent*>(event)->globalPos(),
1768 tab->whatsThis,
this);
1774#ifndef QT_NO_SHORTCUT
1776 case QEvent::Shortcut: {
1777 QShortcutEvent *se =
static_cast<QShortcutEvent *>(event);
1778 for (
int i = 0; i < d->tabList.size(); ++i) {
1779 const QTabBarPrivate::Tab *tab = d->tabList.at(i);
1780 if (tab->shortcutId == se->shortcutId()) {
1789 d->updateMacBorderMetrics();
1791#if QT_CONFIG(draganddrop)
1793 case QEvent::DragEnter:
1794 if (d->changeCurrentOnDrag)
1797 case QEvent::DragMove:
1798 if (d->changeCurrentOnDrag) {
1799 const int tabIndex = tabAt(
static_cast<QDragMoveEvent *>(event)->position().toPoint());
1800 if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1801 d->switchTabCurrentIndex = tabIndex;
1802 d->switchTabTimer.start(
1803 style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay,
nullptr,
this) * 1ms,
this);
1808 case QEvent::DragLeave:
1810 d->killSwitchTabTimer();
1814 case QEvent::MouseButtonPress:
1815 case QEvent::MouseButtonRelease:
1816 case QEvent::MouseMove:
1817 d->mousePosition =
static_cast<QMouseEvent *>(event)->position().toPoint();
1818 d->mouseButtons =
static_cast<QMouseEvent *>(event)->buttons();
1824 return QWidget::event(event);
1843void QTabBar::paintEvent(QPaintEvent *)
1847 QStyleOptionTabBarBase optTabBase;
1848 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1850 QStylePainter p(
this);
1854 bool vertical = verticalTabs(d->shape);
1855 QStyleOptionTab cutTabLeft;
1856 QStyleOptionTab cutTabRight;
1857 selected = d->currentIndex;
1858 if (d->dragInProgress)
1859 selected = d->pressedIndex;
1860 const QRect scrollRect = d->scrollRect();
1861 const QRect normalizedScrollRect = d->normalizedScrollRect();
1863 for (
int i = 0; i < d->tabList.size(); ++i)
1864 optTabBase.tabBarRect |= tabRect(i);
1866 optTabBase.selectedTabRect = tabRect(selected);
1869 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1873 if (d->leftB->isVisible() || d->rightB->isVisible()) {
1876 QRegion buttonRegion;
1877 if (d->leftB->isVisible()) {
1878 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt,
this);
1881 if (d->rightB->isVisible()) {
1882 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt,
this);
1885 if (!buttonRegion.isEmpty())
1886 p.setClipRegion(QRegion(rect()) - buttonRegion);
1889 for (
int i = 0; i < d->tabList.size(); ++i) {
1890 const auto tab = d->tabList.at(i);
1893 for (
const auto side : { QTabBar::LeftSide, QTabBar::RightSide }) {
1894 if (
auto closeButton = qobject_cast<CloseButton *>(tabButton(i, side)))
1895 closeButton->setParentClipRect(scrollRect);
1897 QStyleOptionTab tabOption;
1898 initStyleOption(&tabOption, i);
1899 if (tab->dragOffset != 0) {
1901 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1903 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1906 if (!(tabOption.state & QStyle::State_Enabled)) {
1907 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
1912 QRect tabRect = tab->rect;
1913 int tabStart = vertical ? tabRect.top() : tabRect.left();
1914 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1915 if (tabStart < normalizedScrollRect.left() + d->scrollOffset) {
1917 cutTabLeft = tabOption;
1918 }
else if (tabEnd > normalizedScrollRect.right() + d->scrollOffset) {
1920 cutTabRight = tabOption;
1924 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1925 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1928 optTabBase.tabBarRect |= tabOption.rect;
1932 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1936 if (selected >= 0) {
1937 QStyleOptionTab tabOption;
1938 const auto tab = d->tabList.at(selected);
1939 initStyleOption(&tabOption, selected);
1941 if (tab->dragOffset != 0) {
1944 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1947 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1949 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1953 const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr,
this);
1954 const QRect &movingRect = verticalTabs(d->shape)
1955 ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
1956 : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
1960 if (d->dragInProgress)
1961 d->movingTab->setGeometry(movingRect);
1963 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1967 if (d->leftB->isVisible() && cutLeft >= 0) {
1968 cutTabLeft.rect = rect();
1969 cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft,
this);
1970 p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1973 if (d->rightB->isVisible() && cutRight >= 0) {
1974 cutTabRight.rect = rect();
1975 cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight,
this);
1976 p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
2058void QTabBar::moveTab(
int from,
int to)
2062 || !d->validIndex(from)
2063 || !d->validIndex(to))
2066 auto &fromTab = *d->tabList.at(from);
2067 auto &toTab = *d->tabList.at(to);
2069 bool vertical = verticalTabs(d->shape);
2070 int oldPressedPosition = 0;
2071 if (d->pressedIndex != -1) {
2073 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2074 : d->tabList.at(d->pressedIndex)->rect.x();
2078 int start = qMin(from, to);
2079 int end = qMax(from, to);
2080 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2083 bool rtl = isRightToLeft();
2084 for (
int i = start; i <= end; ++i) {
2087 auto &tab = *d->tabList.at(i);
2089 tab.rect.moveTop(tab.rect.y() + width);
2091 tab.rect.moveLeft(tab.rect.x() + width);
2093 if (rtl && !vertical)
2095 if (tab.dragOffset != 0)
2096 tab.dragOffset += (direction * width);
2101 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2103 fromTab.rect.moveTop(toTab.rect.top() - width);
2106 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2108 fromTab.rect.moveLeft(toTab.rect.left() - width);
2112 d->tabList.move(from, to);
2115 for (
const auto tab : std::as_const(d->tabList))
2116 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2119 int previousIndex = d->currentIndex;
2120 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2123 if (d->pressedIndex != -1) {
2124 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2125 const auto pressedTab = d->tabList.at(d->pressedIndex);
2126 int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
2127 int diff = oldPressedPosition - newPressedPosition;
2128 if (isRightToLeft() && !vertical)
2131 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2133 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2136 d->layoutWidgets(start);
2138 emit tabMoved(from, to);
2139 if (previousIndex != d->currentIndex)
2140 emit currentChanged(d->currentIndex);
2141 emit tabLayoutChange();
2173void QTabBar::mousePressEvent(QMouseEvent *event)
2177 const QPoint pos = event->position().toPoint();
2178 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2179 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2180 if (!isEventInCornerButtons) {
2181 const int index = d->indexAtPos(pos);
2182 emit tabBarClicked(index);
2185 if (event->button() != Qt::LeftButton) {
2190 if (d->pressedIndex != -1 && d->movable)
2191 d->moveTabFinished(d->pressedIndex);
2193 d->pressedIndex = d->indexAtPos(event->position().toPoint());
2195 if (d->validIndex(d->pressedIndex)) {
2196 QStyleOptionTabBarBase optTabBase;
2197 optTabBase.initFrom(
this);
2198 optTabBase.documentMode = d->documentMode;
2199 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this))
2200 setCurrentIndex(d->pressedIndex);
2202 repaint(tabRect(d->pressedIndex));
2204 d->dragStartPosition = event->position().toPoint();
2211void QTabBar::mouseMoveEvent(QMouseEvent *event)
2216 if (d->pressedIndex != -1
2217 && event->buttons() == Qt::NoButton)
2218 d->moveTabFinished(d->pressedIndex);
2221 if (!d->dragInProgress && d->pressedIndex != -1) {
2222 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2223 d->dragInProgress =
true;
2224 d->setupMovableTab();
2228 if (event->buttons() == Qt::LeftButton
2229 && d->dragInProgress
2230 && d->validIndex(d->pressedIndex)) {
2231 bool vertical = verticalTabs(d->shape);
2234 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2236 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2238 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2240 QRect startingRect = tabRect(d->pressedIndex);
2242 startingRect.moveTop(startingRect.y() + dragDistance);
2244 startingRect.moveLeft(startingRect.x() + dragDistance);
2247 if (dragDistance < 0)
2248 overIndex = tabAt(startingRect.topLeft());
2250 overIndex = tabAt(startingRect.topRight());
2252 if (overIndex != d->pressedIndex && overIndex != -1) {
2254 if (isRightToLeft() && !vertical)
2256 if (dragDistance < 0) {
2260 for (
int i = d->pressedIndex;
2261 offset > 0 ? i < overIndex : i > overIndex;
2263 QRect overIndexRect = tabRect(overIndex);
2264 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2265 if (dragDistance > needsToBeOver)
2266 d->slide(i + offset, d->pressedIndex);
2270 if (d->pressedIndex != -1)
2271 d->layoutTab(d->pressedIndex);
2277 if (event->buttons() != Qt::LeftButton) {
2283void QTabBarPrivate::setupMovableTab()
2287 movingTab =
new QMovableTabWidget(q);
2289 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr ,q);
2290 QRect grabRect = q->tabRect(pressedIndex);
2291 if (verticalTabs(shape))
2292 grabRect.adjust(0, -taboverlap, 0, taboverlap);
2294 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2296 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2297 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2298 grabImage.fill(Qt::transparent);
2299 QStylePainter p(&grabImage, q);
2301 QStyleOptionTab tab;
2302 q->initStyleOption(&tab, pressedIndex);
2303 tab.position = QStyleOptionTab::Moving;
2304 if (verticalTabs(shape))
2305 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2307 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2308 p.drawControl(QStyle::CE_TabBarTab, tab);
2311 movingTab->setPixmap(grabImage);
2312 movingTab->setGeometry(grabRect);
2316 const auto &pressedTab = *tabList.at(pressedIndex);
2317 if (pressedTab.leftWidget)
2318 pressedTab.leftWidget->raise();
2319 if (pressedTab.rightWidget)
2320 pressedTab.rightWidget->raise();
2325 movingTab->setVisible(
true);
2363void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2367 if (d->closeButtonOnTabs && event->button() == Qt::MiddleButton) {
2368 const int index = tabAt(event->pos());
2370 emit tabCloseRequested(index);
2375 if (event->button() != Qt::LeftButton) {
2380 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2381 int length = d->tabList.at(d->pressedIndex)->dragOffset;
2382 int width = verticalTabs(d->shape)
2383 ? tabRect(d->pressedIndex).height()
2384 : tabRect(d->pressedIndex).width();
2385 int duration = qMin(ANIMATION_DURATION,
2386 (qAbs(length) * ANIMATION_DURATION) / width);
2387 d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
2388 d->dragInProgress =
false;
2389 d->movingTab->setVisible(
false);
2390 d->dragStartPosition = QPoint();
2394 int oldPressedIndex = d->pressedIndex;
2395 int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
2396 d->pressedIndex = -1;
2397 QStyleOptionTabBarBase optTabBase;
2398 optTabBase.initFrom(
this);
2399 optTabBase.documentMode = d->documentMode;
2400 const bool selectOnRelease =
2401 (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this) == QEvent::MouseButtonRelease);
2402 if (selectOnRelease)
2404 if (d->validIndex(oldPressedIndex))
2405 update(tabRect(oldPressedIndex));