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;
404void QTabBarPrivate::layoutTabs()
408 QSize size = q->size();
411 bool vertTabs = verticalTabs(shape);
412 int tabChainIndex = 0;
414 const auto *style = q->style();
416 Qt::Alignment tabAlignment = Qt::Alignment(style->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
417 QList<QLayoutStruct> tabChain(tabList.size() + 2);
421 tabChain[tabChainIndex].init();
422 tabChain[tabChainIndex].expansive = (!expanding)
423 && (tabAlignment != Qt::AlignLeft)
424 && (tabAlignment != Qt::AlignJustify);
425 tabChain[tabChainIndex].empty =
true;
440 for (
int i = 0; i < tabList.size(); ++i) {
441 const auto tab = tabList.at(i);
446 QSize sz = q->tabSizeHint(i);
447 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
449 maxHeight = qMax(maxHeight, sz.height());
450 sz = q->minimumTabSizeHint(i);
451 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
453 tabChain[tabChainIndex].init();
454 tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
455 tabChain[tabChainIndex].minimumSize = sz.width();
456 tabChain[tabChainIndex].empty =
false;
457 tabChain[tabChainIndex].expansive =
true;
460 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
465 available = size.width();
466 maxExtent = maxHeight;
471 for (
int i = 0; i < tabList.size(); ++i) {
472 auto tab = tabList.at(i);
477 QSize sz = q->tabSizeHint(i);
478 tab->maxRect = QRect(0, y, sz.width(), sz.height());
480 maxWidth = qMax(maxWidth, sz.width());
481 sz = q->minimumTabSizeHint(i);
482 tab->minRect = QRect(0, miny, sz.width(), sz.height());
484 tabChain[tabChainIndex].init();
485 tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
486 tabChain[tabChainIndex].minimumSize = sz.height();
487 tabChain[tabChainIndex].empty =
false;
488 tabChain[tabChainIndex].expansive =
true;
491 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
496 available = size.height();
497 maxExtent = maxWidth;
501 tabChain[tabChainIndex].init();
502 tabChain[tabChainIndex].expansive = (!expanding)
503 && (tabAlignment != Qt::AlignRight)
504 && (tabAlignment != Qt::AlignJustify);
505 tabChain[tabChainIndex].empty =
true;
506 Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs);
509 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
513 for (
int i = 0; i < tabList.size(); ++i) {
514 auto tab = tabList.at(i);
520 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
522 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
524 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
527 if (useScrollButtons && tabList.size() && last > available) {
528 const QRect scrollRect = normalizedScrollRect(0);
533 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
534 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
535 int scrollButtonWidth = style->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
537 const auto setIconOr = [](QToolButton *tb,
const QIcon &icon, Qt::ArrowType arrowType) {
539 tb->setArrowType(arrowType);
547 scrollButtonLeftRect.setHeight(scrollButtonWidth);
548 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
549 scrollButtonRightRect.setHeight(scrollButtonWidth);
550 const auto upIcon = style->standardIcon(QStyle::SP_TabScrollUpButton, &opt, q);
551 const auto downIcon = style->standardIcon(QStyle::SP_TabScrollDownButton, &opt, q);
552 setIconOr(leftB, upIcon, Qt::UpArrow);
553 setIconOr(rightB, downIcon, 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 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
559 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
560 setIconOr(leftB, rightIcon, Qt::RightArrow);
561 setIconOr(rightB, leftIcon, Qt::LeftArrow);
563 scrollButtonLeftRect.setWidth(scrollButtonWidth);
564 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
565 scrollButtonRightRect.setWidth(scrollButtonWidth);
566 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
567 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
568 setIconOr(leftB, leftIcon, Qt::LeftArrow);
569 setIconOr(rightB, rightIcon, Qt::RightArrow);
572 leftB->setGeometry(scrollButtonLeftRect);
573 leftB->setEnabled(
false);
576 rightB->setGeometry(scrollButtonRightRect);
577 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
585 q->tabLayoutChange();
594QRect QTabBarPrivate::normalizedScrollRect(
int index)
const
604 if (leftB->isHidden())
605 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
608 q->initStyleOption(&opt, currentIndex);
609 opt.rect = q->rect();
611 const auto style = q->style();
612 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
613 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
614 QRect tearLeftRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
615 QRect tearRightRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
617 if (verticalTabs(shape)) {
618 int topEdge, bottomEdge;
619 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
620 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
622 if (leftButtonIsOnTop && rightButtonIsOnTop) {
623 topEdge = scrollButtonRightRect.bottom() + 1;
624 bottomEdge = q->height();
625 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
627 bottomEdge = scrollButtonLeftRect.top();
629 topEdge = scrollButtonLeftRect.bottom() + 1;
630 bottomEdge = scrollButtonRightRect.top();
633 const auto lastTab = lastVisibleTab();
636 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
637 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != lastTab->rect.bottom() + 1 - scrollOffset;
638 if (tearTopVisible && !tearLeftRect.isNull())
639 topEdge = tearLeftRect.bottom() + 1;
640 if (tearBottomVisible && !tearRightRect.isNull())
641 bottomEdge = tearRightRect.top();
643 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
645 if (q->layoutDirection() == Qt::RightToLeft) {
646 scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
647 scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
648 tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
649 tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
652 int leftEdge, rightEdge;
653 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
654 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
656 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
657 leftEdge = scrollButtonRightRect.right() + 1;
658 rightEdge = q->width();
659 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
661 rightEdge = scrollButtonLeftRect.left();
663 leftEdge = scrollButtonLeftRect.right() + 1;
664 rightEdge = scrollButtonRightRect.left();
667 const auto lastTab = lastVisibleTab();
670 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
671 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != lastTab->rect.right() + 1 - scrollOffset;
672 if (tearLeftVisible && !tearLeftRect.isNull())
673 leftEdge = tearLeftRect.right() + 1;
674 if (tearRightVisible && !tearRightRect.isNull())
675 rightEdge = tearRightRect.left();
677 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
690void QTabBarPrivate::makeVisible(
int index)
693 if (!validIndex(index))
696 const auto lastTab = lastVisibleTab();
697 const QRect tabRect = tabList.at(index)->rect;
698 const int oldScrollOffset = scrollOffset;
699 const bool horiz = !verticalTabs(shape);
700 const int available = horiz ? q->width() : q->height();
701 const int tabStart = horiz ? tabRect.left() : tabRect.top();
702 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
703 const int lastTabEnd = lastTab ? (horiz ? lastTab->rect.right() : lastTab->rect.bottom()) : 0;
704 const QRect scrollRect = normalizedScrollRect(index);
705 const QRect entireScrollRect = normalizedScrollRect(0);
706 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
707 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
709 if (available >= lastTabEnd) {
712 }
else if (tabStart < scrolledTabBarStart) {
714 scrollOffset = tabStart - scrollRect.left();
715 }
else if (tabEnd > scrolledTabBarEnd) {
717 scrollOffset = qMax(0, tabEnd - scrollRect.right());
718 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
720 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
723 leftB->setEnabled(scrollOffset > -scrollRect.left());
724 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
726 if (oldScrollOffset != scrollOffset) {
978int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
981 if (!d->validIndex(index)) {
982 index = d->tabList.size();
983 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
985 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
987#ifndef QT_NO_SHORTCUT
988 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
990 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
992 if (d->tabList.size() == 1)
993 setCurrentIndex(index);
994 else if (index <= d->currentIndex)
997 if (index <= d->lastVisible)
1000 d->lastVisible = index;
1002 if (d->closeButtonOnTabs) {
1003 QStyleOptionTab opt;
1004 initStyleOption(&opt, index);
1005 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
1006 QAbstractButton *closeButton =
new CloseButton(
this);
1007 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
1008 d, &QTabBarPrivate::closeTab);
1009 setTabButton(index, closeSide, closeButton);
1012 for (
const auto tab : std::as_const(d->tabList)) {
1013 if (tab->lastTab >= index)
1017 if (isVisible() && tabAt(d->mousePosition) == index) {
1018 if (d->scrollRect(index).contains(d->mousePosition)) {
1019 d->hoverIndex = index;
1020 d->hoverRect = tabRect(index);
1023 d->hoverRect = QRect();
1038void QTabBar::removeTab(
int index)
1041 if (d->validIndex(index)) {
1042 auto removedTab = d->tabList.at(index);
1043 if (d->dragInProgress)
1044 d->moveTabFinished(d->pressedIndex);
1046#ifndef QT_NO_SHORTCUT
1047 releaseShortcut(d->tabList.at(index)->shortcutId);
1049 if (removedTab->leftWidget) {
1050 removedTab->leftWidget->hide();
1051 removedTab->leftWidget->deleteLater();
1052 removedTab->leftWidget =
nullptr;
1054 if (removedTab->rightWidget) {
1055 removedTab->rightWidget->hide();
1056 removedTab->rightWidget->deleteLater();
1057 removedTab->rightWidget =
nullptr;
1060 int newIndex = removedTab->lastTab;
1061 d->tabList.removeAt(index);
1063 for (
auto tab : std::as_const(d->tabList)) {
1064 if (tab->lastTab == index)
1066 if (tab->lastTab > index)
1070 d->calculateFirstLastVisible(index,
false,
true);
1072 if (index == d->currentIndex) {
1076 d->currentIndex = -1;
1077 if (d->tabList.size() > 0) {
1078 switch(d->selectionBehaviorOnRemove) {
1079 case SelectPreviousTab:
1080 if (newIndex > index)
1082 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1085 case SelectRightTab:
1086 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1089 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1095 if (d->validIndex(newIndex)) {
1097 int bump = d->tabList.at(newIndex)->lastTab;
1098 setCurrentIndex(newIndex);
1099 d->tabList.at(newIndex)->lastTab = bump;
1102 emit currentChanged(-1);
1105 emit currentChanged(-1);
1107 }
else if (index < d->currentIndex) {
1108 setCurrentIndex(d->currentIndex - 1);
1112 if (d->hoverRect.isValid()) {
1113 update(d->hoverRect);
1114 d->hoverIndex = tabAt(d->mousePosition);
1115 if (d->validIndex(d->hoverIndex)
1116 && d->scrollRect(d->hoverIndex).contains(d->mousePosition)) {
1117 d->hoverRect = tabRect(d->hoverIndex);
1118 update(d->hoverRect);
1120 d->hoverRect = QRect();
1598QSize QTabBar::tabSizeHint(
int index)
const
1602 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1603 QStyleOptionTab opt;
1604 d->initBasicStyleOption(&opt, index);
1605 opt.text = tab->text;
1606 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1607 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt,
this);
1608 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt,
this);
1609 const QFontMetrics fm = fontMetrics();
1611 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1612 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1614 int widgetWidth = 0;
1615 int widgetHeight = 0;
1617 if (!opt.leftButtonSize.isEmpty()) {
1619 widgetWidth += opt.leftButtonSize.width();
1620 widgetHeight += opt.leftButtonSize.height();
1622 if (!opt.rightButtonSize.isEmpty()) {
1624 widgetWidth += opt.rightButtonSize.width();
1625 widgetHeight += opt.rightButtonSize.height();
1627 if (!opt.icon.isNull())
1630 QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1631 if (it == d->textSizes.end())
1632 it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1633 const int textWidth = it.value().width();
1635 if (verticalTabs(d->shape)) {
1636 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1637 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1639 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1640 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1643 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1705bool QTabBar::event(QEvent *event)
1708 switch (event->type()) {
1709 case QEvent::HoverMove:
1710 case QEvent::HoverEnter: {
1711 QHoverEvent *he =
static_cast<QHoverEvent *>(event);
1712 d->mousePosition = he->position().toPoint();
1713 const auto sr = d->scrollRect();
1714 const auto oldHoverRect = d->hoverRect & sr;
1715 if (!oldHoverRect.contains(d->mousePosition)) {
1716 if (d->hoverRect.isValid())
1717 update(d->hoverRect);
1718 d->hoverIndex = tabAt(d->mousePosition);
1719 if (d->validIndex(d->hoverIndex) && sr.contains(d->mousePosition)) {
1720 d->hoverRect = tabRect(d->hoverIndex);
1721 update(d->hoverRect);
1723 d->hoverRect = QRect();
1729 case QEvent::HoverLeave: {
1730 d->mousePosition = {-1, -1};
1731 if (d->hoverRect.isValid())
1732 update(d->hoverRect);
1734 d->hoverRect = QRect();
1735#if QT_CONFIG(wheelevent)
1736 d->accumulatedAngleDelta = QPoint();
1740#if QT_CONFIG(tooltip)
1741 case QEvent::ToolTip:
1742 if (
const QTabBarPrivate::Tab *tab = d->at(tabAt(
static_cast<QHelpEvent*>(event)->pos()))) {
1743 if (!tab->toolTip.isEmpty()) {
1744 QToolTip::showText(
static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip,
this);
1750#if QT_CONFIG(whatsthis)
1751 case QEvent::QEvent::QueryWhatsThis: {
1752 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()));
1753 if (!tab || tab->whatsThis.isEmpty())
1757 case QEvent::WhatsThis:
1758 if (
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()))) {
1759 if (!tab->whatsThis.isEmpty()) {
1760 QWhatsThis::showText(
static_cast<QHelpEvent*>(event)->globalPos(),
1761 tab->whatsThis,
this);
1767#ifndef QT_NO_SHORTCUT
1769 case QEvent::Shortcut: {
1770 QShortcutEvent *se =
static_cast<QShortcutEvent *>(event);
1771 for (
int i = 0; i < d->tabList.size(); ++i) {
1772 const QTabBarPrivate::Tab *tab = d->tabList.at(i);
1773 if (tab->shortcutId == se->shortcutId()) {
1782 d->updateMacBorderMetrics();
1784#if QT_CONFIG(draganddrop)
1786 case QEvent::DragEnter:
1787 if (d->changeCurrentOnDrag)
1790 case QEvent::DragMove:
1791 if (d->changeCurrentOnDrag) {
1792 const int tabIndex = tabAt(
static_cast<QDragMoveEvent *>(event)->position().toPoint());
1793 if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1794 d->switchTabCurrentIndex = tabIndex;
1795 d->switchTabTimer.start(
1796 style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay,
nullptr,
this) * 1ms,
this);
1801 case QEvent::DragLeave:
1803 d->killSwitchTabTimer();
1807 case QEvent::MouseButtonPress:
1808 case QEvent::MouseButtonRelease:
1809 case QEvent::MouseMove:
1810 d->mousePosition =
static_cast<QMouseEvent *>(event)->position().toPoint();
1811 d->mouseButtons =
static_cast<QMouseEvent *>(event)->buttons();
1817 return QWidget::event(event);
1836void QTabBar::paintEvent(QPaintEvent *)
1840 QStyleOptionTabBarBase optTabBase;
1841 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1843 QStylePainter p(
this);
1847 bool vertical = verticalTabs(d->shape);
1848 QStyleOptionTab cutTabLeft;
1849 QStyleOptionTab cutTabRight;
1850 selected = d->currentIndex;
1851 if (d->dragInProgress)
1852 selected = d->pressedIndex;
1853 const QRect scrollRect = d->scrollRect();
1854 const QRect normalizedScrollRect = d->normalizedScrollRect();
1856 for (
int i = 0; i < d->tabList.size(); ++i)
1857 optTabBase.tabBarRect |= tabRect(i);
1859 optTabBase.selectedTabRect = tabRect(selected);
1862 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1866 if (d->leftB->isVisible() || d->rightB->isVisible()) {
1869 QRegion buttonRegion;
1870 if (d->leftB->isVisible()) {
1871 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt,
this);
1874 if (d->rightB->isVisible()) {
1875 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt,
this);
1878 if (!buttonRegion.isEmpty())
1879 p.setClipRegion(QRegion(rect()) - buttonRegion);
1882 for (
int i = 0; i < d->tabList.size(); ++i) {
1883 const auto tab = d->tabList.at(i);
1886 for (
const auto side : { QTabBar::LeftSide, QTabBar::RightSide }) {
1887 if (
auto closeButton = qobject_cast<CloseButton *>(tabButton(i, side)))
1888 closeButton->setParentClipRect(scrollRect);
1890 QStyleOptionTab tabOption;
1891 initStyleOption(&tabOption, i);
1892 if (tab->dragOffset != 0) {
1894 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1896 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1899 if (!(tabOption.state & QStyle::State_Enabled)) {
1900 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
1905 QRect tabRect = tab->rect;
1906 int tabStart = vertical ? tabRect.top() : tabRect.left();
1907 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1908 if (tabStart < normalizedScrollRect.left() + d->scrollOffset) {
1910 cutTabLeft = tabOption;
1911 }
else if (tabEnd > normalizedScrollRect.right() + d->scrollOffset) {
1913 cutTabRight = tabOption;
1917 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1918 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1921 optTabBase.tabBarRect |= tabOption.rect;
1925 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1929 if (selected >= 0) {
1930 QStyleOptionTab tabOption;
1931 const auto tab = d->tabList.at(selected);
1932 initStyleOption(&tabOption, selected);
1934 if (tab->dragOffset != 0) {
1937 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1940 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1942 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1946 const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr,
this);
1947 const QRect &movingRect = verticalTabs(d->shape)
1948 ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
1949 : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
1953 if (d->dragInProgress)
1954 d->movingTab->setGeometry(movingRect);
1956 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1960 if (d->leftB->isVisible() && cutLeft >= 0) {
1961 cutTabLeft.rect = rect();
1962 cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft,
this);
1963 p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1966 if (d->rightB->isVisible() && cutRight >= 0) {
1967 cutTabRight.rect = rect();
1968 cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight,
this);
1969 p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
2051void QTabBar::moveTab(
int from,
int to)
2055 || !d->validIndex(from)
2056 || !d->validIndex(to))
2059 auto &fromTab = *d->tabList.at(from);
2060 auto &toTab = *d->tabList.at(to);
2062 bool vertical = verticalTabs(d->shape);
2063 int oldPressedPosition = 0;
2064 if (d->pressedIndex != -1) {
2066 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2067 : d->tabList.at(d->pressedIndex)->rect.x();
2071 int start = qMin(from, to);
2072 int end = qMax(from, to);
2073 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2076 bool rtl = isRightToLeft();
2077 for (
int i = start; i <= end; ++i) {
2080 auto &tab = *d->tabList.at(i);
2082 tab.rect.moveTop(tab.rect.y() + width);
2084 tab.rect.moveLeft(tab.rect.x() + width);
2086 if (rtl && !vertical)
2088 if (tab.dragOffset != 0)
2089 tab.dragOffset += (direction * width);
2094 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2096 fromTab.rect.moveTop(toTab.rect.top() - width);
2099 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2101 fromTab.rect.moveLeft(toTab.rect.left() - width);
2105 d->tabList.move(from, to);
2108 for (
const auto tab : std::as_const(d->tabList))
2109 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2112 int previousIndex = d->currentIndex;
2113 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2116 if (d->pressedIndex != -1) {
2117 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2118 const auto pressedTab = d->tabList.at(d->pressedIndex);
2119 int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
2120 int diff = oldPressedPosition - newPressedPosition;
2121 if (isRightToLeft() && !vertical)
2124 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2126 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2129 d->layoutWidgets(start);
2131 emit tabMoved(from, to);
2132 if (previousIndex != d->currentIndex)
2133 emit currentChanged(d->currentIndex);
2134 emit tabLayoutChange();
2166void QTabBar::mousePressEvent(QMouseEvent *event)
2170 const QPoint pos = event->position().toPoint();
2171 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2172 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2173 if (!isEventInCornerButtons) {
2174 const int index = d->indexAtPos(pos);
2175 emit tabBarClicked(index);
2178 if (event->button() != Qt::LeftButton) {
2183 if (d->pressedIndex != -1 && d->movable)
2184 d->moveTabFinished(d->pressedIndex);
2186 d->pressedIndex = d->indexAtPos(event->position().toPoint());
2188 if (d->validIndex(d->pressedIndex)) {
2189 QStyleOptionTabBarBase optTabBase;
2190 optTabBase.initFrom(
this);
2191 optTabBase.documentMode = d->documentMode;
2192 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this))
2193 setCurrentIndex(d->pressedIndex);
2195 repaint(tabRect(d->pressedIndex));
2197 d->dragStartPosition = event->position().toPoint();
2204void QTabBar::mouseMoveEvent(QMouseEvent *event)
2209 if (d->pressedIndex != -1
2210 && event->buttons() == Qt::NoButton)
2211 d->moveTabFinished(d->pressedIndex);
2214 if (!d->dragInProgress && d->pressedIndex != -1) {
2215 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2216 d->dragInProgress =
true;
2217 d->setupMovableTab();
2221 if (event->buttons() == Qt::LeftButton
2222 && d->dragInProgress
2223 && d->validIndex(d->pressedIndex)) {
2224 bool vertical = verticalTabs(d->shape);
2227 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2229 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2231 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2233 QRect startingRect = tabRect(d->pressedIndex);
2235 startingRect.moveTop(startingRect.y() + dragDistance);
2237 startingRect.moveLeft(startingRect.x() + dragDistance);
2240 if (dragDistance < 0)
2241 overIndex = tabAt(startingRect.topLeft());
2243 overIndex = tabAt(startingRect.topRight());
2245 if (overIndex != d->pressedIndex && overIndex != -1) {
2247 if (isRightToLeft() && !vertical)
2249 if (dragDistance < 0) {
2253 for (
int i = d->pressedIndex;
2254 offset > 0 ? i < overIndex : i > overIndex;
2256 QRect overIndexRect = tabRect(overIndex);
2257 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2258 if (dragDistance > needsToBeOver)
2259 d->slide(i + offset, d->pressedIndex);
2263 if (d->pressedIndex != -1)
2264 d->layoutTab(d->pressedIndex);
2270 if (event->buttons() != Qt::LeftButton) {
2276void QTabBarPrivate::setupMovableTab()
2280 movingTab =
new QMovableTabWidget(q);
2282 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr ,q);
2283 QRect grabRect = q->tabRect(pressedIndex);
2284 if (verticalTabs(shape))
2285 grabRect.adjust(0, -taboverlap, 0, taboverlap);
2287 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2289 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2290 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2291 grabImage.fill(Qt::transparent);
2292 QStylePainter p(&grabImage, q);
2294 QStyleOptionTab tab;
2295 q->initStyleOption(&tab, pressedIndex);
2296 tab.position = QStyleOptionTab::Moving;
2297 if (verticalTabs(shape))
2298 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2300 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2301 p.drawControl(QStyle::CE_TabBarTab, tab);
2304 movingTab->setPixmap(grabImage);
2305 movingTab->setGeometry(grabRect);
2309 const auto &pressedTab = *tabList.at(pressedIndex);
2310 if (pressedTab.leftWidget)
2311 pressedTab.leftWidget->raise();
2312 if (pressedTab.rightWidget)
2313 pressedTab.rightWidget->raise();
2318 movingTab->setVisible(
true);
2356void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2360 if (d->closeButtonOnTabs && event->button() == Qt::MiddleButton) {
2361 const int index = tabAt(event->pos());
2363 emit tabCloseRequested(index);
2368 if (event->button() != Qt::LeftButton) {
2373 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2374 int length = d->tabList.at(d->pressedIndex)->dragOffset;
2375 int width = verticalTabs(d->shape)
2376 ? tabRect(d->pressedIndex).height()
2377 : tabRect(d->pressedIndex).width();
2378 int duration = qMin(ANIMATION_DURATION,
2379 (qAbs(length) * ANIMATION_DURATION) / width);
2380 d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
2381 d->dragInProgress =
false;
2382 d->movingTab->setVisible(
false);
2383 d->dragStartPosition = QPoint();
2387 int oldPressedIndex = d->pressedIndex;
2388 int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
2389 d->pressedIndex = -1;
2390 QStyleOptionTabBarBase optTabBase;
2391 optTabBase.initFrom(
this);
2392 optTabBase.documentMode = d->documentMode;
2393 const bool selectOnRelease =
2394 (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this) == QEvent::MouseButtonRelease);
2395 if (selectOnRelease)
2397 if (d->validIndex(oldPressedIndex))
2398 update(tabRect(oldPressedIndex));