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());
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;
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);
421void QTabBarPrivate::layoutTabs()
425 QSize size = q->size();
428 bool vertTabs = verticalTabs(shape);
429 int tabChainIndex = 0;
431 const auto *style = q->style();
433 Qt::Alignment tabAlignment = Qt::Alignment(style->styleHint(QStyle::SH_TabBar_Alignment,
nullptr, q));
434 QList<QLayoutStruct> tabChain(tabList.size() + 2);
438 tabChain[tabChainIndex].init();
439 tabChain[tabChainIndex].expansive = (!expanding)
440 && (tabAlignment != Qt::AlignLeft)
441 && (tabAlignment != Qt::AlignJustify);
442 tabChain[tabChainIndex].empty =
true;
457 for (
int i = 0; i < tabList.size(); ++i) {
458 const auto tab = tabList.at(i);
463 QSize sz = q->tabSizeHint(i);
464 tab->maxRect = QRect(x, 0, sz.width(), sz.height());
466 maxHeight = qMax(maxHeight, sz.height());
467 sz = q->minimumTabSizeHint(i);
468 tab->minRect = QRect(minx, 0, sz.width(), sz.height());
470 tabChain[tabChainIndex].init();
471 tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
472 tabChain[tabChainIndex].minimumSize = sz.width();
473 tabChain[tabChainIndex].empty =
false;
474 tabChain[tabChainIndex].expansive =
true;
477 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
482 available = size.width();
483 maxExtent = maxHeight;
488 for (
int i = 0; i < tabList.size(); ++i) {
489 auto tab = tabList.at(i);
494 QSize sz = q->tabSizeHint(i);
495 tab->maxRect = QRect(0, y, sz.width(), sz.height());
497 maxWidth = qMax(maxWidth, sz.width());
498 sz = q->minimumTabSizeHint(i);
499 tab->minRect = QRect(0, miny, sz.width(), sz.height());
501 tabChain[tabChainIndex].init();
502 tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
503 tabChain[tabChainIndex].minimumSize = sz.height();
504 tabChain[tabChainIndex].empty =
false;
505 tabChain[tabChainIndex].expansive =
true;
508 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
513 available = size.height();
514 maxExtent = maxWidth;
518 tabChain[tabChainIndex].init();
519 tabChain[tabChainIndex].expansive = (!expanding)
520 && (tabAlignment != Qt::AlignRight)
521 && (tabAlignment != Qt::AlignJustify);
522 tabChain[tabChainIndex].empty =
true;
523 Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs);
526 qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
530 for (
int i = 0; i < tabList.size(); ++i) {
531 auto tab = tabList.at(i);
537 const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
539 tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
541 tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
544 if (useScrollButtons && tabList.size() && last > available) {
545 const QRect scrollRect = normalizedScrollRect(0);
550 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
551 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
552 int scrollButtonWidth = style->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
554 const auto setIconOr = [](QToolButton *tb,
const QIcon &icon, Qt::ArrowType arrowType) {
556 tb->setArrowType(arrowType);
564 scrollButtonLeftRect.setHeight(scrollButtonWidth);
565 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
566 scrollButtonRightRect.setHeight(scrollButtonWidth);
567 const auto upIcon = style->standardIcon(QStyle::SP_TabScrollUpButton, &opt, q);
568 const auto downIcon = style->standardIcon(QStyle::SP_TabScrollDownButton, &opt, q);
569 setIconOr(leftB, upIcon, Qt::UpArrow);
570 setIconOr(rightB, downIcon, Qt::DownArrow);
571 }
else if (q->layoutDirection() == Qt::RightToLeft) {
572 scrollButtonRightRect.setWidth(scrollButtonWidth);
573 scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
574 scrollButtonLeftRect.setWidth(scrollButtonWidth);
575 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
576 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
577 setIconOr(leftB, rightIcon, Qt::RightArrow);
578 setIconOr(rightB, leftIcon, Qt::LeftArrow);
580 scrollButtonLeftRect.setWidth(scrollButtonWidth);
581 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
582 scrollButtonRightRect.setWidth(scrollButtonWidth);
583 const auto leftIcon = style->standardIcon(QStyle::SP_TabScrollLeftButton, &opt, q);
584 const auto rightIcon = style->standardIcon(QStyle::SP_TabScrollRightButton, &opt, q);
585 setIconOr(leftB, leftIcon, Qt::LeftArrow);
586 setIconOr(rightB, rightIcon, Qt::RightArrow);
589 leftB->setGeometry(scrollButtonLeftRect);
590 leftB->setEnabled(
false);
593 rightB->setGeometry(scrollButtonRightRect);
594 rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
602 q->tabLayoutChange();
605QRect QTabBarPrivate::normalizedScrollRect(
int index)
615 if (leftB->isHidden())
616 return verticalTabs(shape) ? q->rect().transposed() : q->rect();
619 q->initStyleOption(&opt, currentIndex);
620 opt.rect = q->rect();
622 const auto style = q->style();
623 QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
624 QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
625 QRect tearLeftRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
626 QRect tearRightRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
628 if (verticalTabs(shape)) {
629 int topEdge, bottomEdge;
630 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
631 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
633 if (leftButtonIsOnTop && rightButtonIsOnTop) {
634 topEdge = scrollButtonRightRect.bottom() + 1;
635 bottomEdge = q->height();
636 }
else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
638 bottomEdge = scrollButtonLeftRect.top();
640 topEdge = scrollButtonLeftRect.bottom() + 1;
641 bottomEdge = scrollButtonRightRect.top();
644 const auto lastTab = lastVisibleTab();
647 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
648 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != lastTab->rect.bottom() + 1 - scrollOffset;
649 if (tearTopVisible && !tearLeftRect.isNull())
650 topEdge = tearLeftRect.bottom() + 1;
651 if (tearBottomVisible && !tearRightRect.isNull())
652 bottomEdge = tearRightRect.top();
654 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
656 if (q->layoutDirection() == Qt::RightToLeft) {
657 scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
658 scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
659 tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
660 tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
663 int leftEdge, rightEdge;
664 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
665 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
667 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
668 leftEdge = scrollButtonRightRect.right() + 1;
669 rightEdge = q->width();
670 }
else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
672 rightEdge = scrollButtonLeftRect.left();
674 leftEdge = scrollButtonLeftRect.right() + 1;
675 rightEdge = scrollButtonRightRect.left();
678 const auto lastTab = lastVisibleTab();
681 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
682 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != lastTab->rect.right() + 1 - scrollOffset;
683 if (tearLeftVisible && !tearLeftRect.isNull())
684 leftEdge = tearLeftRect.right() + 1;
685 if (tearRightVisible && !tearRightRect.isNull())
686 rightEdge = tearRightRect.left();
688 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
701void QTabBarPrivate::makeVisible(
int index)
704 if (!validIndex(index))
707 const auto lastTab = lastVisibleTab();
708 const QRect tabRect = tabList.at(index)->rect;
709 const int oldScrollOffset = scrollOffset;
710 const bool horiz = !verticalTabs(shape);
711 const int available = horiz ? q->width() : q->height();
712 const int tabStart = horiz ? tabRect.left() : tabRect.top();
713 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
714 const int lastTabEnd = lastTab ? (horiz ? lastTab->rect.right() : lastTab->rect.bottom()) : 0;
715 const QRect scrollRect = normalizedScrollRect(index);
716 const QRect entireScrollRect = normalizedScrollRect(0);
717 const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
718 const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
720 if (available >= lastTabEnd) {
723 }
else if (tabStart < scrolledTabBarStart) {
725 scrollOffset = tabStart - scrollRect.left();
726 }
else if (tabEnd > scrolledTabBarEnd) {
728 scrollOffset = qMax(0, tabEnd - scrollRect.right());
729 }
else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
731 scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
734 leftB->setEnabled(scrollOffset > -scrollRect.left());
735 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
737 if (oldScrollOffset != scrollOffset) {
989int QTabBar::insertTab(
int index,
const QIcon& icon,
const QString &text)
992 if (!d->validIndex(index)) {
993 index = d->tabList.size();
994 d->tabList.append(
new QTabBarPrivate::Tab(icon, text));
996 d->tabList.insert(index,
new QTabBarPrivate::Tab(icon, text));
998#ifndef QT_NO_SHORTCUT
999 d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1001 d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
1003 if (d->tabList.size() == 1)
1004 setCurrentIndex(index);
1005 else if (index <= d->currentIndex)
1008 if (index <= d->lastVisible)
1011 d->lastVisible = index;
1013 if (d->closeButtonOnTabs) {
1014 QStyleOptionTab opt;
1015 initStyleOption(&opt, index);
1016 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition,
nullptr,
this);
1017 QAbstractButton *closeButton =
new CloseButton(
this);
1018 QObjectPrivate::connect(closeButton, &CloseButton::clicked,
1019 d, &QTabBarPrivate::closeTab);
1020 setTabButton(index, closeSide, closeButton);
1023 for (
const auto tab : std::as_const(d->tabList)) {
1024 if (tab->lastTab >= index)
1028 if (isVisible() && tabAt(d->mousePosition) == index) {
1029 if (d->normalizedScrollRect(index).contains(d->mousePosition)) {
1030 d->hoverIndex = index;
1031 d->hoverRect = tabRect(index);
1034 d->hoverRect = QRect();
1049void QTabBar::removeTab(
int index)
1052 if (d->validIndex(index)) {
1053 auto removedTab = d->tabList.at(index);
1054 if (d->dragInProgress)
1055 d->moveTabFinished(d->pressedIndex);
1057#ifndef QT_NO_SHORTCUT
1058 releaseShortcut(d->tabList.at(index)->shortcutId);
1060 if (removedTab->leftWidget) {
1061 removedTab->leftWidget->hide();
1062 removedTab->leftWidget->deleteLater();
1063 removedTab->leftWidget =
nullptr;
1065 if (removedTab->rightWidget) {
1066 removedTab->rightWidget->hide();
1067 removedTab->rightWidget->deleteLater();
1068 removedTab->rightWidget =
nullptr;
1071 int newIndex = removedTab->lastTab;
1072 d->tabList.removeAt(index);
1074 for (
auto tab : std::as_const(d->tabList)) {
1075 if (tab->lastTab == index)
1077 if (tab->lastTab > index)
1081 d->calculateFirstLastVisible(index,
false,
true);
1083 if (index == d->currentIndex) {
1087 d->currentIndex = -1;
1088 if (d->tabList.size() > 0) {
1089 switch(d->selectionBehaviorOnRemove) {
1090 case SelectPreviousTab:
1091 if (newIndex > index)
1093 if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
1096 case SelectRightTab:
1097 newIndex = qBound(d->firstVisible, index, d->lastVisible);
1100 newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1106 if (d->validIndex(newIndex)) {
1108 int bump = d->tabList.at(newIndex)->lastTab;
1109 setCurrentIndex(newIndex);
1110 d->tabList.at(newIndex)->lastTab = bump;
1113 emit currentChanged(-1);
1116 emit currentChanged(-1);
1118 }
else if (index < d->currentIndex) {
1119 setCurrentIndex(d->currentIndex - 1);
1123 if (d->hoverRect.isValid()) {
1124 update(d->hoverRect);
1125 d->hoverIndex = tabAt(d->mousePosition);
1126 if (d->validIndex(d->hoverIndex)
1127 && d->normalizedScrollRect(d->hoverIndex).contains(d->mousePosition)) {
1128 d->hoverRect = tabRect(d->hoverIndex);
1129 update(d->hoverRect);
1131 d->hoverRect = QRect();
1609QSize QTabBar::tabSizeHint(
int index)
const
1613 if (
const QTabBarPrivate::Tab *tab = d->at(index)) {
1614 QStyleOptionTab opt;
1615 d->initBasicStyleOption(&opt, index);
1616 opt.text = tab->text;
1617 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1618 int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt,
this);
1619 int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt,
this);
1620 const QFontMetrics fm = fontMetrics();
1622 int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1623 int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1625 int widgetWidth = 0;
1626 int widgetHeight = 0;
1628 if (!opt.leftButtonSize.isEmpty()) {
1630 widgetWidth += opt.leftButtonSize.width();
1631 widgetHeight += opt.leftButtonSize.height();
1633 if (!opt.rightButtonSize.isEmpty()) {
1635 widgetWidth += opt.rightButtonSize.width();
1636 widgetHeight += opt.rightButtonSize.height();
1638 if (!opt.icon.isNull())
1641 QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1642 if (it == d->textSizes.end())
1643 it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1644 const int textWidth = it.value().width();
1646 if (verticalTabs(d->shape)) {
1647 csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1648 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1650 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1651 qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1654 QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz,
this);
1716bool QTabBar::event(QEvent *event)
1719 switch (event->type()) {
1720 case QEvent::HoverMove:
1721 case QEvent::HoverEnter: {
1722 QHoverEvent *he =
static_cast<QHoverEvent *>(event);
1723 d->mousePosition = he->position().toPoint();
1724 const auto sr = d->normalizedScrollRect();
1725 const auto oldHoverRect = d->hoverRect & sr;
1726 if (!oldHoverRect.contains(d->mousePosition)) {
1727 if (d->hoverRect.isValid())
1728 update(d->hoverRect);
1729 d->hoverIndex = tabAt(d->mousePosition);
1730 if (d->validIndex(d->hoverIndex) && sr.contains(d->mousePosition)) {
1731 d->hoverRect = tabRect(d->hoverIndex);
1732 update(d->hoverRect);
1734 d->hoverRect = QRect();
1740 case QEvent::HoverLeave: {
1741 d->mousePosition = {-1, -1};
1742 if (d->hoverRect.isValid())
1743 update(d->hoverRect);
1745 d->hoverRect = QRect();
1746#if QT_CONFIG(wheelevent)
1747 d->accumulatedAngleDelta = QPoint();
1751#if QT_CONFIG(tooltip)
1752 case QEvent::ToolTip:
1753 if (
const QTabBarPrivate::Tab *tab = d->at(tabAt(
static_cast<QHelpEvent*>(event)->pos()))) {
1754 if (!tab->toolTip.isEmpty()) {
1755 QToolTip::showText(
static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip,
this);
1761#if QT_CONFIG(whatsthis)
1762 case QEvent::QEvent::QueryWhatsThis: {
1763 const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()));
1764 if (!tab || tab->whatsThis.isEmpty())
1768 case QEvent::WhatsThis:
1769 if (
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(
static_cast<QHelpEvent*>(event)->pos()))) {
1770 if (!tab->whatsThis.isEmpty()) {
1771 QWhatsThis::showText(
static_cast<QHelpEvent*>(event)->globalPos(),
1772 tab->whatsThis,
this);
1778#ifndef QT_NO_SHORTCUT
1780 case QEvent::Shortcut: {
1781 QShortcutEvent *se =
static_cast<QShortcutEvent *>(event);
1782 for (
int i = 0; i < d->tabList.size(); ++i) {
1783 const QTabBarPrivate::Tab *tab = d->tabList.at(i);
1784 if (tab->shortcutId == se->shortcutId()) {
1793 d->updateMacBorderMetrics();
1795#if QT_CONFIG(draganddrop)
1797 case QEvent::DragEnter:
1798 if (d->changeCurrentOnDrag)
1801 case QEvent::DragMove:
1802 if (d->changeCurrentOnDrag) {
1803 const int tabIndex = tabAt(
static_cast<QDragMoveEvent *>(event)->position().toPoint());
1804 if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1805 d->switchTabCurrentIndex = tabIndex;
1806 d->switchTabTimer.start(
1807 style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay,
nullptr,
this) * 1ms,
this);
1812 case QEvent::DragLeave:
1814 d->killSwitchTabTimer();
1818 case QEvent::MouseButtonPress:
1819 case QEvent::MouseButtonRelease:
1820 case QEvent::MouseMove:
1821 d->mousePosition =
static_cast<QMouseEvent *>(event)->position().toPoint();
1822 d->mouseButtons =
static_cast<QMouseEvent *>(event)->buttons();
1828 return QWidget::event(event);
1847void QTabBar::paintEvent(QPaintEvent *)
1851 QStyleOptionTabBarBase optTabBase;
1852 QTabBarPrivate::initStyleBaseOption(&optTabBase,
this, size());
1854 QStylePainter p(
this);
1858 bool vertical = verticalTabs(d->shape);
1859 QStyleOptionTab cutTabLeft;
1860 QStyleOptionTab cutTabRight;
1861 selected = d->currentIndex;
1862 if (d->dragInProgress)
1863 selected = d->pressedIndex;
1864 const QRect scrollRect = d->normalizedScrollRect();
1866 for (
int i = 0; i < d->tabList.size(); ++i)
1867 optTabBase.tabBarRect |= tabRect(i);
1869 optTabBase.selectedTabRect = tabRect(selected);
1872 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1876 if (d->leftB->isVisible() || d->rightB->isVisible()) {
1879 QRegion buttonRegion;
1880 if (d->leftB->isVisible()) {
1881 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt,
this);
1884 if (d->rightB->isVisible()) {
1885 const auto r = style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt,
this);
1888 if (!buttonRegion.isEmpty())
1889 p.setClipRegion(QRegion(rect()) - buttonRegion);
1892 for (
int i = 0; i < d->tabList.size(); ++i) {
1893 const auto tab = d->tabList.at(i);
1896 for (
const auto side : { QTabBar::LeftSide, QTabBar::RightSide }) {
1897 if (
auto closeButton = qobject_cast<CloseButton *>(tabButton(i, side)))
1898 closeButton->setParentClipRect(scrollRect);
1900 QStyleOptionTab tabOption;
1901 initStyleOption(&tabOption, i);
1902 if (tab->dragOffset != 0) {
1904 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1906 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1909 if (!(tabOption.state & QStyle::State_Enabled)) {
1910 tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
1915 QRect tabRect = tab->rect;
1916 int tabStart = vertical ? tabRect.top() : tabRect.left();
1917 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1918 if (tabStart < scrollRect.left() + d->scrollOffset) {
1920 cutTabLeft = tabOption;
1921 }
else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1923 cutTabRight = tabOption;
1927 if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
1928 || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
1931 optTabBase.tabBarRect |= tabOption.rect;
1935 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1939 if (selected >= 0) {
1940 QStyleOptionTab tabOption;
1941 const auto tab = d->tabList.at(selected);
1942 initStyleOption(&tabOption, selected);
1944 if (tab->dragOffset != 0) {
1947 tabOption.position = QStyleOptionTab::TabPosition::Moving;
1950 tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
1952 tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
1956 const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr,
this);
1957 const QRect &movingRect = verticalTabs(d->shape)
1958 ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
1959 : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
1963 if (d->dragInProgress)
1964 d->movingTab->setGeometry(movingRect);
1966 p.drawControl(QStyle::CE_TabBarTab, tabOption);
1970 if (d->leftB->isVisible() && cutLeft >= 0) {
1971 cutTabLeft.rect = rect();
1972 cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft,
this);
1973 p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1976 if (d->rightB->isVisible() && cutRight >= 0) {
1977 cutTabRight.rect = rect();
1978 cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight,
this);
1979 p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
2061void QTabBar::moveTab(
int from,
int to)
2065 || !d->validIndex(from)
2066 || !d->validIndex(to))
2069 auto &fromTab = *d->tabList.at(from);
2070 auto &toTab = *d->tabList.at(to);
2072 bool vertical = verticalTabs(d->shape);
2073 int oldPressedPosition = 0;
2074 if (d->pressedIndex != -1) {
2076 oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
2077 : d->tabList.at(d->pressedIndex)->rect.x();
2081 int start = qMin(from, to);
2082 int end = qMax(from, to);
2083 int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
2086 bool rtl = isRightToLeft();
2087 for (
int i = start; i <= end; ++i) {
2090 auto &tab = *d->tabList.at(i);
2092 tab.rect.moveTop(tab.rect.y() + width);
2094 tab.rect.moveLeft(tab.rect.x() + width);
2096 if (rtl && !vertical)
2098 if (tab.dragOffset != 0)
2099 tab.dragOffset += (direction * width);
2104 fromTab.rect.moveTop(toTab.rect.bottom() + 1);
2106 fromTab.rect.moveTop(toTab.rect.top() - width);
2109 fromTab.rect.moveLeft(toTab.rect.right() + 1);
2111 fromTab.rect.moveLeft(toTab.rect.left() - width);
2115 d->tabList.move(from, to);
2118 for (
const auto tab : std::as_const(d->tabList))
2119 tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
2122 int previousIndex = d->currentIndex;
2123 d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2126 if (d->pressedIndex != -1) {
2127 d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2128 const auto pressedTab = d->tabList.at(d->pressedIndex);
2129 int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
2130 int diff = oldPressedPosition - newPressedPosition;
2131 if (isRightToLeft() && !vertical)
2134 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2136 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2139 d->layoutWidgets(start);
2141 emit tabMoved(from, to);
2142 if (previousIndex != d->currentIndex)
2143 emit currentChanged(d->currentIndex);
2144 emit tabLayoutChange();
2176void QTabBar::mousePressEvent(QMouseEvent *event)
2180 const QPoint pos = event->position().toPoint();
2181 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2182 || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2183 if (!isEventInCornerButtons) {
2184 const int index = d->indexAtPos(pos);
2185 emit tabBarClicked(index);
2188 if (event->button() != Qt::LeftButton) {
2193 if (d->pressedIndex != -1 && d->movable)
2194 d->moveTabFinished(d->pressedIndex);
2196 d->pressedIndex = d->indexAtPos(event->position().toPoint());
2198 if (d->validIndex(d->pressedIndex)) {
2199 QStyleOptionTabBarBase optTabBase;
2200 optTabBase.initFrom(
this);
2201 optTabBase.documentMode = d->documentMode;
2202 if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this))
2203 setCurrentIndex(d->pressedIndex);
2205 repaint(tabRect(d->pressedIndex));
2207 d->dragStartPosition = event->position().toPoint();
2214void QTabBar::mouseMoveEvent(QMouseEvent *event)
2219 if (d->pressedIndex != -1
2220 && event->buttons() == Qt::NoButton)
2221 d->moveTabFinished(d->pressedIndex);
2224 if (!d->dragInProgress && d->pressedIndex != -1) {
2225 if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2226 d->dragInProgress =
true;
2227 d->setupMovableTab();
2231 if (event->buttons() == Qt::LeftButton
2232 && d->dragInProgress
2233 && d->validIndex(d->pressedIndex)) {
2234 bool vertical = verticalTabs(d->shape);
2237 dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
2239 dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
2241 d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
2243 QRect startingRect = tabRect(d->pressedIndex);
2245 startingRect.moveTop(startingRect.y() + dragDistance);
2247 startingRect.moveLeft(startingRect.x() + dragDistance);
2250 if (dragDistance < 0)
2251 overIndex = tabAt(startingRect.topLeft());
2253 overIndex = tabAt(startingRect.topRight());
2255 if (overIndex != d->pressedIndex && overIndex != -1) {
2257 if (isRightToLeft() && !vertical)
2259 if (dragDistance < 0) {
2263 for (
int i = d->pressedIndex;
2264 offset > 0 ? i < overIndex : i > overIndex;
2266 QRect overIndexRect = tabRect(overIndex);
2267 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2268 if (dragDistance > needsToBeOver)
2269 d->slide(i + offset, d->pressedIndex);
2273 if (d->pressedIndex != -1)
2274 d->layoutTab(d->pressedIndex);
2280 if (event->buttons() != Qt::LeftButton) {
2286void QTabBarPrivate::setupMovableTab()
2290 movingTab =
new QMovableTabWidget(q);
2292 int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap,
nullptr ,q);
2293 QRect grabRect = q->tabRect(pressedIndex);
2294 if (verticalTabs(shape))
2295 grabRect.adjust(0, -taboverlap, 0, taboverlap);
2297 grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2299 QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
2300 grabImage.setDevicePixelRatio(q->devicePixelRatio());
2301 grabImage.fill(Qt::transparent);
2302 QStylePainter p(&grabImage, q);
2304 QStyleOptionTab tab;
2305 q->initStyleOption(&tab, pressedIndex);
2306 tab.position = QStyleOptionTab::Moving;
2307 if (verticalTabs(shape))
2308 tab.rect.moveTopLeft(QPoint(0, taboverlap));
2310 tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2311 p.drawControl(QStyle::CE_TabBarTab, tab);
2314 movingTab->setPixmap(grabImage);
2315 movingTab->setGeometry(grabRect);
2319 const auto &pressedTab = *tabList.at(pressedIndex);
2320 if (pressedTab.leftWidget)
2321 pressedTab.leftWidget->raise();
2322 if (pressedTab.rightWidget)
2323 pressedTab.rightWidget->raise();
2328 movingTab->setVisible(
true);
2366void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2370 if (d->closeButtonOnTabs && event->button() == Qt::MiddleButton) {
2371 const int index = tabAt(event->pos());
2373 emit tabCloseRequested(index);
2378 if (event->button() != Qt::LeftButton) {
2383 if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2384 int length = d->tabList.at(d->pressedIndex)->dragOffset;
2385 int width = verticalTabs(d->shape)
2386 ? tabRect(d->pressedIndex).height()
2387 : tabRect(d->pressedIndex).width();
2388 int duration = qMin(ANIMATION_DURATION,
2389 (qAbs(length) * ANIMATION_DURATION) / width);
2390 d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
2391 d->dragInProgress =
false;
2392 d->movingTab->setVisible(
false);
2393 d->dragStartPosition = QPoint();
2397 int oldPressedIndex = d->pressedIndex;
2398 int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
2399 d->pressedIndex = -1;
2400 QStyleOptionTabBarBase optTabBase;
2401 optTabBase.initFrom(
this);
2402 optTabBase.documentMode = d->documentMode;
2403 const bool selectOnRelease =
2404 (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase,
this) == QEvent::MouseButtonRelease);
2405 if (selectOnRelease)
2407 if (d->validIndex(oldPressedIndex))
2408 update(tabRect(oldPressedIndex));