summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qtabbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets/qtabbar.cpp')
-rw-r--r--src/widgets/widgets/qtabbar.cpp1015
1 files changed, 616 insertions, 399 deletions
diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp
index 68453e1842..0b463840ae 100644
--- a/src/widgets/widgets/qtabbar.cpp
+++ b/src/widgets/widgets/qtabbar.cpp
@@ -1,49 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "private/qlayoutengine_p.h"
#if QT_CONFIG(itemviews)
#include "qabstractitemdelegate.h"
#endif
#include "qapplication.h"
-#include "qbitmap.h"
-#include "qcursor.h"
#include "qevent.h"
#include "qpainter.h"
#include "qstyle.h"
@@ -52,40 +14,40 @@
#if QT_CONFIG(tabwidget)
#include "qtabwidget.h"
#endif
+#if QT_CONFIG(tooltip)
#include "qtooltip.h"
+#endif
#if QT_CONFIG(whatsthis)
#include "qwhatsthis.h"
#endif
#include "private/qtextengine_p.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
#include "qaccessible.h"
#endif
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
#include <qpa/qplatformnativeinterface.h>
#endif
#include "qdebug.h"
+#include "private/qapplication_p.h"
#include "private/qtabbar_p.h"
-#if 0 // Used to be included in Qt4 for Q_WS_MAC
-#include <private/qt_mac_p.h>
-#include <private/qt_cocoa_helpers_mac_p.h>
-#endif
-
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace {
class CloseButton : public QAbstractButton
{
Q_OBJECT
public:
- explicit CloseButton(QWidget *parent = 0);
+ explicit CloseButton(QWidget *parent = nullptr);
QSize sizeHint() const override;
QSize minimumSizeHint() const override
{ return sizeHint(); }
- void enterEvent(QEvent *event) override;
+ void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
};
@@ -109,17 +71,9 @@ void QMovableTabWidget::paintEvent(QPaintEvent *e)
p.drawPixmap(0, 0, m_pixmap);
}
-inline static bool verticalTabs(QTabBar::Shape shape)
-{
- return shape == QTabBar::RoundedWest
- || shape == QTabBar::RoundedEast
- || shape == QTabBar::TriangularWest
- || shape == QTabBar::TriangularEast;
-}
-
void QTabBarPrivate::updateMacBorderMetrics()
{
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
Q_Q(QTabBar);
// Extend the unified title and toolbar area to cover the tab bar iff
// 1) the tab bar is in document mode
@@ -141,6 +95,8 @@ void QTabBarPrivate::updateMacBorderMetrics()
}
QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ if (!nativeInterface)
+ return;
quintptr identifier = reinterpret_cast<quintptr>(q);
// Set geometry
@@ -174,7 +130,7 @@ void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex)
if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
return;
- const QTabBarPrivate::Tab &tab = tabList.at(tabIndex);
+ const QTabBarPrivate::Tab &tab = *tabList.at(tabIndex);
option->initFrom(q);
option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
option->rect = q->tabRect(tabIndex);
@@ -211,8 +167,8 @@ void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex)
else
option->selectedPosition = QStyleOptionTab::NotAdjacent;
- const bool paintBeginning = (tabIndex == 0) || (dragInProgress && tabIndex == pressedIndex + 1);
- const bool paintEnd = (tabIndex == totalTabs - 1) || (dragInProgress && tabIndex == pressedIndex - 1);
+ const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
+ const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
if (paintBeginning) {
if (paintEnd)
option->position = QStyleOptionTab::OnlyOneTab;
@@ -233,6 +189,7 @@ void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex)
option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
}
#endif
+ option->tabIndex = tabIndex;
}
/*!
@@ -318,9 +275,9 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
returns the visual geometry of a single tab.
\table 100%
- \row \li \inlineimage fusion-tabbar.png Screenshot of a Fusion style tab bar
+ \row \li \inlineimage {fusion-tabbar.png} {Screenshot of a Fusion style tab bar}
\li A tab bar shown in the \l{Qt Widget Gallery}{Fusion widget style}.
- \row \li \inlineimage fusion-tabbar-truncated.png Screenshot of a truncated Fusion tab bar
+ \row \li \inlineimage {fusion-tabbar-truncated.png} {Screenshot of a truncated Fusion tab bar}
\li A truncated tab bar shown in the Fusion widget style.
\endtable
@@ -407,15 +364,19 @@ void QTabBarPrivate::init()
{
Q_Q(QTabBar);
leftB = new QToolButton(q);
+ leftB->setObjectName(u"ScrollLeftButton"_s);
leftB->setAutoRepeat(true);
- QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ QObjectPrivate::connect(leftB, &QToolButton::clicked,
+ this, &QTabBarPrivate::scrollTabs);
leftB->hide();
rightB = new QToolButton(q);
+ rightB->setObjectName(u"ScrollRightButton"_s);
rightB->setAutoRepeat(true);
- QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ QObjectPrivate::connect(rightB, &QToolButton::clicked,
+ this, &QTabBarPrivate::scrollTabs);
rightB->hide();
#ifdef QT_KEYPAD_NAVIGATION
- if (QApplication::keypadNavigationEnabled()) {
+ if (QApplicationPrivate::keypadNavigationEnabled()) {
leftB->setFocusPolicy(Qt::NoFocus);
rightB->setFocusPolicy(Qt::NoFocus);
q->setFocusPolicy(Qt::NoFocus);
@@ -423,23 +384,13 @@ void QTabBarPrivate::init()
#endif
q->setFocusPolicy(Qt::TabFocus);
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
leftB->setAccessibleName(QTabBar::tr("Scroll Left"));
rightB->setAccessibleName(QTabBar::tr("Scroll Right"));
#endif
q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
- useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
-}
-
-QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
-{
- return validIndex(index)?&tabList[index]:0;
-}
-
-const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
-{
- return validIndex(index)?&tabList[index]:0;
+ elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, nullptr, q));
+ useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, nullptr, q);
}
int QTabBarPrivate::indexAtPos(const QPoint &p) const
@@ -447,8 +398,8 @@ int QTabBarPrivate::indexAtPos(const QPoint &p) const
Q_Q(const QTabBar);
if (q->tabRect(currentIndex).contains(p))
return currentIndex;
- for (int i = 0; i < tabList.count(); ++i)
- if (tabList.at(i).enabled && q->tabRect(i).contains(p))
+ for (int i = 0; i < tabList.size(); ++i)
+ if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
return i;
return -1;
}
@@ -460,12 +411,12 @@ void QTabBarPrivate::layoutTabs()
QSize size = q->size();
int last, available;
int maxExtent;
- int i;
bool vertTabs = verticalTabs(shape);
int tabChainIndex = 0;
+ int hiddenTabs = 0;
- Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
- QVector<QLayoutStruct> tabChain(tabList.count() + 2);
+ Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, nullptr, q));
+ QList<QLayoutStruct> tabChain(tabList.size() + 2);
// We put an empty item at the front and back and set its expansive attribute
// depending on tabAlignment and expanding.
@@ -488,22 +439,28 @@ void QTabBarPrivate::layoutTabs()
int minx = 0;
int x = 0;
int maxHeight = 0;
- for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
+ for (int i = 0; i < tabList.size(); ++i) {
+ const auto tab = tabList.at(i);
+ if (!tab->visible) {
+ ++hiddenTabs;
+ continue;
+ }
QSize sz = q->tabSizeHint(i);
- tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
+ tab->maxRect = QRect(x, 0, sz.width(), sz.height());
x += sz.width();
maxHeight = qMax(maxHeight, sz.height());
sz = q->minimumTabSizeHint(i);
- tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
+ tab->minRect = QRect(minx, 0, sz.width(), sz.height());
minx += sz.width();
tabChain[tabChainIndex].init();
- tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
+ tabChain[tabChainIndex].sizeHint = tab->maxRect.width();
tabChain[tabChainIndex].minimumSize = sz.width();
tabChain[tabChainIndex].empty = false;
tabChain[tabChainIndex].expansive = true;
if (!expanding)
tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
+ ++tabChainIndex;
}
last = minx;
@@ -513,22 +470,28 @@ void QTabBarPrivate::layoutTabs()
int miny = 0;
int y = 0;
int maxWidth = 0;
- for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
+ for (int i = 0; i < tabList.size(); ++i) {
+ auto tab = tabList.at(i);
+ if (!tab->visible) {
+ ++hiddenTabs;
+ continue;
+ }
QSize sz = q->tabSizeHint(i);
- tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
+ tab->maxRect = QRect(0, y, sz.width(), sz.height());
y += sz.height();
maxWidth = qMax(maxWidth, sz.width());
sz = q->minimumTabSizeHint(i);
- tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
+ tab->minRect = QRect(0, miny, sz.width(), sz.height());
miny += sz.height();
tabChain[tabChainIndex].init();
- tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
+ tabChain[tabChainIndex].sizeHint = tab->maxRect.height();
tabChain[tabChainIndex].minimumSize = sz.height();
tabChain[tabChainIndex].empty = false;
tabChain[tabChainIndex].expansive = true;
if (!expanding)
tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
+ ++tabChainIndex;
}
last = miny;
@@ -542,27 +505,33 @@ void QTabBarPrivate::layoutTabs()
&& (tabAlignment != Qt::AlignRight)
&& (tabAlignment != Qt::AlignJustify);
tabChain[tabChainIndex].empty = true;
- Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
+ Q_ASSERT(tabChainIndex == tabChain.size() - 1 - hiddenTabs); // add an assert just to make sure.
// Do the calculation
- qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
+ qGeomCalc(tabChain, 0, tabChain.size(), 0, qMax(available, last), 0);
// Use the results
- for (i = 0; i < tabList.count(); ++i) {
- const QLayoutStruct &lstruct = tabChain.at(i + 1);
+ hiddenTabs = 0;
+ for (int i = 0; i < tabList.size(); ++i) {
+ auto tab = tabList.at(i);
+ if (!tab->visible) {
+ tab->rect = QRect();
+ ++hiddenTabs;
+ continue;
+ }
+ const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
if (!vertTabs)
- tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
+ tab->rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
else
- tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
+ tab->rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
}
- if (useScrollButtons && tabList.count() && last > available) {
+ if (useScrollButtons && tabList.size() && last > available) {
const QRect scrollRect = normalizedScrollRect(0);
- scrollOffset = -scrollRect.left();
Q_Q(QTabBar);
QStyleOption opt;
- opt.init(q);
+ opt.initFrom(q);
QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
@@ -595,10 +564,9 @@ void QTabBarPrivate::layoutTabs()
leftB->show();
rightB->setGeometry(scrollButtonRightRect);
- rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width());
+ rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
rightB->show();
} else {
- scrollOffset = 0;
rightB->hide();
leftB->hide();
}
@@ -615,6 +583,11 @@ QRect QTabBarPrivate::normalizedScrollRect(int index)
// tab bar itself is in a different orientation.
Q_Q(QTabBar);
+ // If scrollbuttons are not visible, then there's no tear either, and
+ // the entire widget is the scroll rect.
+ if (leftB->isHidden())
+ return verticalTabs(shape) ? q->rect().transposed() : q->rect();
+
QStyleOptionTab opt;
q->initStyleOption(&opt, currentIndex);
opt.rect = q->rect();
@@ -641,7 +614,7 @@ QRect QTabBarPrivate::normalizedScrollRect(int index)
}
bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
- bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.constLast().rect.bottom() + 1 - scrollOffset;
+ bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.constLast()->rect.bottom() + 1 - scrollOffset;
if (tearTopVisible && !tearLeftRect.isNull())
topEdge = tearLeftRect.bottom() + 1;
if (tearBottomVisible && !tearRightRect.isNull())
@@ -672,7 +645,7 @@ QRect QTabBarPrivate::normalizedScrollRect(int index)
}
bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
- bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.constLast().rect.right() + 1 - scrollOffset;
+ bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.constLast()->rect.right() + 1 - scrollOffset;
if (tearLeftVisible && !tearLeftRect.isNull())
leftEdge = tearLeftRect.right() + 1;
if (tearRightVisible && !tearRightRect.isNull())
@@ -694,25 +667,33 @@ int QTabBarPrivate::hoveredTabIndex() const
void QTabBarPrivate::makeVisible(int index)
{
Q_Q(QTabBar);
- if (!validIndex(index) || leftB->isHidden())
+ if (!validIndex(index))
return;
- const QRect tabRect = tabList.at(index).rect;
+ const QRect tabRect = tabList.at(index)->rect;
const int oldScrollOffset = scrollOffset;
const bool horiz = !verticalTabs(shape);
+ const int available = horiz ? q->width() : q->height();
const int tabStart = horiz ? tabRect.left() : tabRect.top();
const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
- const int lastTabEnd = horiz ? tabList.constLast().rect.right() : tabList.constLast().rect.bottom();
+ const int lastTabEnd = horiz ? tabList.constLast()->rect.right() : tabList.constLast()->rect.bottom();
const QRect scrollRect = normalizedScrollRect(index);
+ const QRect entireScrollRect = normalizedScrollRect(0); // ignore tears
const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
- if (tabStart < scrolledTabBarStart) {
+ if (available >= lastTabEnd) {
+ // the entire tabbar fits, reset scroll
+ scrollOffset = 0;
+ } else if (tabStart < scrolledTabBarStart) {
// Tab is outside on the left, so scroll left.
scrollOffset = tabStart - scrollRect.left();
} else if (tabEnd > scrolledTabBarEnd) {
// Tab is outside on the right, so scroll right.
- scrollOffset = tabEnd - scrollRect.right();
+ scrollOffset = qMax(0, tabEnd - scrollRect.right());
+ } else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
+ // fill any free space on the right without overshooting
+ scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
}
leftB->setEnabled(scrollOffset > -scrollRect.left());
@@ -739,34 +720,34 @@ void QTabBarPrivate::layoutTab(int index)
Q_Q(QTabBar);
Q_ASSERT(index >= 0);
- Tab &tab = tabList[index];
+ const Tab *tab = tabList.at(index);
bool vertical = verticalTabs(shape);
- if (!(tab.leftWidget || tab.rightWidget))
+ if (!(tab->leftWidget || tab->rightWidget))
return;
QStyleOptionTab opt;
q->initStyleOption(&opt, index);
- if (tab.leftWidget) {
+ if (tab->leftWidget) {
QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
QPoint p = rect.topLeft();
if ((index == pressedIndex) || paintWithOffsets) {
if (vertical)
- p.setY(p.y() + tabList[index].dragOffset);
+ p.setY(p.y() + tab->dragOffset);
else
- p.setX(p.x() + tabList[index].dragOffset);
+ p.setX(p.x() + tab->dragOffset);
}
- tab.leftWidget->move(p);
+ tab->leftWidget->move(p);
}
- if (tab.rightWidget) {
+ if (tab->rightWidget) {
QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
QPoint p = rect.topLeft();
if ((index == pressedIndex) || paintWithOffsets) {
if (vertical)
- p.setY(p.y() + tab.dragOffset);
+ p.setY(p.y() + tab->dragOffset);
else
- p.setX(p.x() + tab.dragOffset);
+ p.setX(p.x() + tab->dragOffset);
}
- tab.rightWidget->move(p);
+ tab->rightWidget->move(p);
}
}
@@ -786,20 +767,20 @@ void QTabBarPrivate::autoHideTabs()
q->setVisible(q->count() > 1);
}
-void QTabBarPrivate::_q_closeTab()
+void QTabBarPrivate::closeTab()
{
Q_Q(QTabBar);
QObject *object = q->sender();
int tabToClose = -1;
- QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, q);
- for (int i = 0; i < tabList.count(); ++i) {
+ QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, q);
+ for (int i = 0; i < tabList.size(); ++i) {
if (closeSide == QTabBar::LeftSide) {
- if (tabList.at(i).leftWidget == object) {
+ if (tabList.at(i)->leftWidget == object) {
tabToClose = i;
break;
}
} else {
- if (tabList.at(i).rightWidget == object) {
+ if (tabList.at(i)->rightWidget == object) {
tabToClose = i;
break;
}
@@ -809,26 +790,29 @@ void QTabBarPrivate::_q_closeTab()
emit q->tabCloseRequested(tabToClose);
}
-void QTabBarPrivate::_q_scrollTabs()
+void QTabBarPrivate::scrollTabs()
{
Q_Q(QTabBar);
const QObject *sender = q->sender();
const bool horizontal = !verticalTabs(shape);
- const QRect scrollRect = normalizedScrollRect();
+ const QRect scrollRect = normalizedScrollRect().translated(scrollOffset, 0);
+
int i = -1;
if (sender == leftB) {
- for (i = tabList.count() - 1; i >= 0; --i) {
- int start = horizontal ? tabList.at(i).rect.left() : tabList.at(i).rect.top();
- if (start < scrollRect.left() + scrollOffset) {
+ for (i = tabList.size() - 1; i >= 0; --i) {
+ int start = horizontal ? tabList.at(i)->rect.left() : tabList.at(i)->rect.top();
+ if (start < scrollRect.left()) {
makeVisible(i);
return;
}
}
} else if (sender == rightB) {
- for (i = 0; i < tabList.count(); ++i) {
- int end = horizontal ? tabList.at(i).rect.right() : tabList.at(i).rect.bottom();
- if (end > scrollRect.right() + scrollOffset) {
+ for (i = 0; i < tabList.size(); ++i) {
+ const auto tabRect = tabList.at(i)->rect;
+ int start = horizontal ? tabRect.left() : tabRect.top();
+ int end = horizontal ? tabRect.right() : tabRect.bottom();
+ if (end > scrollRect.right() && start > scrollOffset) {
makeVisible(i);
return;
}
@@ -843,7 +827,7 @@ void QTabBarPrivate::refresh()
// be safe in case a subclass is also handling move with the tabs
if (pressedIndex != -1
&& movable
- && QApplication::mouseButtons() == Qt::NoButton) {
+ && mouseButtons == Qt::NoButton) {
moveTabFinished(pressedIndex);
if (!validIndex(pressedIndex))
pressedIndex = -1;
@@ -863,7 +847,7 @@ void QTabBarPrivate::refresh()
Creates a new tab bar with the given \a parent.
*/
QTabBar::QTabBar(QWidget* parent)
- :QWidget(*new QTabBarPrivate, parent, 0)
+ :QWidget(*new QTabBarPrivate, parent, { })
{
Q_D(QTabBar);
d->init();
@@ -904,7 +888,7 @@ void QTabBar::setShape(Shape shape)
\property QTabBar::drawBase
\brief defines whether or not tab bar should draw its base.
- If true then QTabBar draws a base in relation to the styles overlab.
+ If true then QTabBar draws a base in relation to the styles overlap.
Otherwise only the tabs are drawn.
\sa QStyle::pixelMetric(), QStyle::PM_TabBarBaseOverlap, QStyleOptionTabBarBase
@@ -947,7 +931,7 @@ int QTabBar::addTab(const QIcon& icon, const QString &text)
/*!
Inserts a new tab with text \a text at position \a index. If \a
- index is out of range, the new tab is appened. Returns the new
+ index is out of range, the new tab is appended. Returns the new
tab's index.
*/
int QTabBar::insertTab(int index, const QString &text)
@@ -971,32 +955,44 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
{
Q_D(QTabBar);
if (!d->validIndex(index)) {
- index = d->tabList.count();
- d->tabList.append(QTabBarPrivate::Tab(icon, text));
+ index = d->tabList.size();
+ d->tabList.append(new QTabBarPrivate::Tab(icon, text));
} else {
- d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
+ d->tabList.insert(index, new QTabBarPrivate::Tab(icon, text));
}
#ifndef QT_NO_SHORTCUT
- d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
+ d->tabList.at(index)->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
#endif
+ d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
d->refresh();
- if (d->tabList.count() == 1)
+ if (d->tabList.size() == 1)
setCurrentIndex(index);
else if (index <= d->currentIndex)
++d->currentIndex;
+ if (index <= d->lastVisible)
+ ++d->lastVisible;
+ else
+ d->lastVisible = index;
+
if (d->closeButtonOnTabs) {
QStyleOptionTab opt;
initStyleOption(&opt, index);
- ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
+ ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
QAbstractButton *closeButton = new CloseButton(this);
- connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ QObjectPrivate::connect(closeButton, &CloseButton::clicked,
+ d, &QTabBarPrivate::closeTab);
setTabButton(index, closeSide, closeButton);
}
- for (int i = 0; i < d->tabList.count(); ++i) {
- if (d->tabList[i].lastTab >= index)
- ++d->tabList[i].lastTab;
+ for (const auto tab : std::as_const(d->tabList)) {
+ if (tab->lastTab >= index)
+ ++tab->lastTab;
+ }
+
+ if (tabAt(d->mousePosition) == index) {
+ d->hoverIndex = index;
+ d->hoverRect = tabRect(index);
}
tabInserted(index);
@@ -1014,31 +1010,36 @@ void QTabBar::removeTab(int index)
{
Q_D(QTabBar);
if (d->validIndex(index)) {
+ auto removedTab = d->tabList.at(index);
if (d->dragInProgress)
d->moveTabFinished(d->pressedIndex);
#ifndef QT_NO_SHORTCUT
- releaseShortcut(d->tabList.at(index).shortcutId);
+ releaseShortcut(d->tabList.at(index)->shortcutId);
#endif
- if (d->tabList[index].leftWidget) {
- d->tabList[index].leftWidget->hide();
- d->tabList[index].leftWidget->deleteLater();
- d->tabList[index].leftWidget = 0;
+ if (removedTab->leftWidget) {
+ removedTab->leftWidget->hide();
+ removedTab->leftWidget->deleteLater();
+ removedTab->leftWidget = nullptr;
}
- if (d->tabList[index].rightWidget) {
- d->tabList[index].rightWidget->hide();
- d->tabList[index].rightWidget->deleteLater();
- d->tabList[index].rightWidget = 0;
+ if (removedTab->rightWidget) {
+ removedTab->rightWidget->hide();
+ removedTab->rightWidget->deleteLater();
+ removedTab->rightWidget = nullptr;
}
- int newIndex = d->tabList[index].lastTab;
+ int newIndex = removedTab->lastTab;
d->tabList.removeAt(index);
- for (int i = 0; i < d->tabList.count(); ++i) {
- if (d->tabList[i].lastTab == index)
- d->tabList[i].lastTab = -1;
- if (d->tabList[i].lastTab > index)
- --d->tabList[i].lastTab;
+ delete removedTab;
+ for (auto tab : std::as_const(d->tabList)) {
+ if (tab->lastTab == index)
+ tab->lastTab = -1;
+ if (tab->lastTab > index)
+ --tab->lastTab;
}
+
+ d->calculateFirstLastVisible(index, false, true);
+
if (index == d->currentIndex) {
// The current tab is going away, in order to make sure
// we emit that "current has changed", we need to reset this
@@ -1049,18 +1050,14 @@ void QTabBar::removeTab(int index)
case SelectPreviousTab:
if (newIndex > index)
newIndex--;
- if (d->validIndex(newIndex))
+ if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
break;
Q_FALLTHROUGH();
case SelectRightTab:
- newIndex = index;
- if (newIndex >= d->tabList.size())
- newIndex = d->tabList.size() - 1;
+ newIndex = qBound(d->firstVisible, index, d->lastVisible);
break;
case SelectLeftTab:
- newIndex = index - 1;
- if (newIndex < 0)
- newIndex = 0;
+ newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
break;
default:
break;
@@ -1068,9 +1065,12 @@ void QTabBar::removeTab(int index)
if (d->validIndex(newIndex)) {
// don't loose newIndex's old through setCurrentIndex
- int bump = d->tabList[newIndex].lastTab;
+ int bump = d->tabList.at(newIndex)->lastTab;
setCurrentIndex(newIndex);
- d->tabList[newIndex].lastTab = bump;
+ d->tabList.at(newIndex)->lastTab = bump;
+ } else {
+ // we had a valid current index, but there are no visible tabs left
+ emit currentChanged(-1);
}
} else {
emit currentChanged(-1);
@@ -1080,16 +1080,15 @@ void QTabBar::removeTab(int index)
}
d->refresh();
d->autoHideTabs();
- if (!d->hoverRect.isEmpty()) {
- for (int i = 0; i < d->tabList.count(); ++i) {
- const QRect area = tabRect(i);
- if (area.contains(mapFromGlobal(QCursor::pos()))) {
- d->hoverIndex = i;
- d->hoverRect = area;
- break;
- }
- }
+ if (d->hoverRect.isValid()) {
update(d->hoverRect);
+ d->hoverIndex = tabAt(d->mousePosition);
+ if (d->validIndex(d->hoverIndex)) {
+ d->hoverRect = tabRect(d->hoverIndex);
+ update(d->hoverRect);
+ } else {
+ d->hoverRect = QRect();
+ }
}
tabRemoved(index);
}
@@ -1122,9 +1121,52 @@ void QTabBar::setTabEnabled(int index, bool enabled)
#endif
update();
if (!enabled && index == d->currentIndex)
- setCurrentIndex(d->validIndex(index+1)?index+1:0);
- else if (enabled && !d->validIndex(d->currentIndex))
- setCurrentIndex(index);
+ setCurrentIndex(d->selectNewCurrentIndexFrom(index+1));
+ else if (enabled && !isTabVisible(d->currentIndex))
+ setCurrentIndex(d->selectNewCurrentIndexFrom(index));
+ }
+}
+
+
+/*!
+ Returns true if the tab at position \a index is visible; otherwise
+ returns false.
+ \since 5.15
+*/
+bool QTabBar::isTabVisible(int index) const
+{
+ Q_D(const QTabBar);
+ if (d->validIndex(index))
+ return d->tabList.at(index)->visible;
+ return false;
+}
+
+/*!
+ If \a visible is true, make the tab at position \a index visible,
+ otherwise make it hidden.
+ \since 5.15
+*/
+void QTabBar::setTabVisible(int index, bool visible)
+{
+ Q_D(QTabBar);
+ if (QTabBarPrivate::Tab *tab = d->at(index)) {
+ d->layoutDirty = (visible != tab->visible);
+ if (!d->layoutDirty)
+ return;
+ tab->visible = visible;
+ if (tab->leftWidget)
+ tab->leftWidget->setVisible(visible);
+ if (tab->rightWidget)
+ tab->rightWidget->setVisible(visible);
+#ifndef QT_NO_SHORTCUT
+ setShortcutEnabled(tab->shortcutId, visible);
+#endif
+ d->calculateFirstLastVisible(index, visible, false);
+ if (!visible && index == d->currentIndex) {
+ const int newindex = d->selectNewCurrentIndexFrom(index+1);
+ setCurrentIndex(newindex);
+ }
+ update();
}
}
@@ -1217,7 +1259,7 @@ void QTabBar::setTabIcon(int index, const QIcon & icon)
}
}
-#ifndef QT_NO_TOOLTIP
+#if QT_CONFIG(tooltip)
/*!
Sets the tool tip of the tab at position \a index to \a tip.
*/
@@ -1239,7 +1281,7 @@ QString QTabBar::tabToolTip(int index) const
return tab->toolTip;
return QString();
}
-#endif // QT_NO_TOOLTIP
+#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(whatsthis)
/*!
@@ -1295,7 +1337,7 @@ QVariant QTabBar::tabData(int index) const
/*!
Returns the visual rectangle of the tab at position \a
- index, or a null rectangle if \a index is out of range.
+ index, or a null rectangle if \a index is hidden, or out of range.
*/
QRect QTabBar::tabRect(int index) const
{
@@ -1303,6 +1345,8 @@ QRect QTabBar::tabRect(int index) const
if (const QTabBarPrivate::Tab *tab = d->at(index)) {
if (d->layoutDirty)
const_cast<QTabBarPrivate*>(d)->layoutTabs();
+ if (!tab->visible)
+ return QRect();
QRect r = tab->rect;
if (verticalTabs(d->shape))
r.translate(0, -d->scrollOffset);
@@ -1358,17 +1402,30 @@ void QTabBar::setCurrentIndex(int index)
Q_D(QTabBar);
if (d->dragInProgress && d->pressedIndex != -1)
return;
+ if (d->currentIndex == index)
+ return;
int oldIndex = d->currentIndex;
- if (d->validIndex(index) && d->currentIndex != index) {
+ if (auto tab = d->at(index)) {
d->currentIndex = index;
+ // If the size hint depends on whether the tab is selected (for instance a style
+ // sheet rule that sets a bold font on the 'selected' tab) then we need to
+ // re-layout the entire tab bar. To minimize the cost, do that only if the
+ // size hint changes for the tab that becomes the current tab (the old current tab
+ // will most certainly do the same). QTBUG-6905
+ if (tabRect(index).size() != tabSizeHint(index))
+ d->layoutTabs();
update();
- d->makeVisible(index);
- d->tabList[index].lastTab = oldIndex;
- if (oldIndex >= 0 && oldIndex < count())
+ if (!isVisible())
+ d->layoutDirty = true;
+ else
+ d->makeVisible(index);
+ if (d->validIndex(oldIndex)) {
+ tab->lastTab = oldIndex;
d->layoutTab(oldIndex);
+ }
d->layoutTab(index);
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (hasFocus()) {
QAccessibleEvent focusEvent(this, QAccessible::Focus);
@@ -1399,7 +1456,7 @@ QSize QTabBar::iconSize() const
Q_D(const QTabBar);
if (d->iconSize.isValid())
return d->iconSize;
- int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
+ int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, nullptr, this);
return QSize(iconExtent, iconExtent);
}
@@ -1421,7 +1478,7 @@ void QTabBar::setIconSize(const QSize &size)
int QTabBar::count() const
{
Q_D(const QTabBar);
- return d->tabList.count();
+ return d->tabList.size();
}
@@ -1433,10 +1490,11 @@ QSize QTabBar::sizeHint() const
if (d->layoutDirty)
const_cast<QTabBarPrivate*>(d)->layoutTabs();
QRect r;
- for (int i = 0; i < d->tabList.count(); ++i)
- r = r.united(d->tabList.at(i).maxRect);
- QSize sz = QApplication::globalStrut();
- return r.size().expandedTo(sz);
+ for (const auto tab : d->tabList) {
+ if (tab->visible)
+ r = r.united(tab->maxRect);
+ }
+ return r.size();
}
/*!\reimp
@@ -1448,9 +1506,11 @@ QSize QTabBar::minimumSizeHint() const
const_cast<QTabBarPrivate*>(d)->layoutTabs();
if (!d->useScrollButtons) {
QRect r;
- for (int i = 0; i < d->tabList.count(); ++i)
- r = r.united(d->tabList.at(i).minRect);
- return r.size().expandedTo(QApplication::globalStrut());
+ for (const auto tab : d->tabList) {
+ if (tab->visible)
+ r = r.united(tab->minRect);
+ }
+ return r.size();
}
if (verticalTabs(d->shape))
return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
@@ -1461,20 +1521,20 @@ QSize QTabBar::minimumSizeHint() const
// Compute the most-elided possible text, for minimumSizeHint
static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
{
- if (text.length() <= 3)
+ if (text.size() <= 3)
return text;
- static const QLatin1String Ellipses("...");
+ static const auto Ellipses = "..."_L1;
QString ret;
switch (mode) {
case Qt::ElideRight:
- ret = text.leftRef(2) + Ellipses;
+ ret = QStringView{text}.left(2) + Ellipses;
break;
case Qt::ElideMiddle:
- ret = text.leftRef(1) + Ellipses + text.rightRef(1);
+ ret = QStringView{text}.left(1) + Ellipses + QStringView{text}.right(1);
break;
case Qt::ElideLeft:
- ret = Ellipses + text.rightRef(2);
+ ret = Ellipses + QStringView{text}.right(2);
break;
case Qt::ElideNone:
ret = text;
@@ -1491,11 +1551,11 @@ static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
QSize QTabBar::minimumTabSizeHint(int index) const
{
Q_D(const QTabBar);
- QTabBarPrivate::Tab &tab = const_cast<QTabBarPrivate::Tab&>(d->tabList[index]);
- QString oldText = tab.text;
- tab.text = computeElidedText(d->elideMode, oldText);
+ QTabBarPrivate::Tab *tab = d->tabList.at(index);
+ QString oldText = tab->text;
+ tab->text = computeElidedText(d->elideMode, oldText);
QSize size = tabSizeHint(index);
- tab.text = oldText;
+ tab->text = oldText;
return size;
}
@@ -1509,7 +1569,7 @@ QSize QTabBar::tabSizeHint(int index) const
if (const QTabBarPrivate::Tab *tab = d->at(index)) {
QStyleOptionTab opt;
d->initBasicStyleOption(&opt, index);
- opt.text = d->tabList.at(index).text;
+ opt.text = tab->text;
QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
@@ -1561,7 +1621,7 @@ QSize QTabBar::tabSizeHint(int index) const
*/
void QTabBar::tabInserted(int index)
{
- Q_UNUSED(index)
+ Q_UNUSED(index);
}
/*!
@@ -1572,7 +1632,7 @@ void QTabBar::tabInserted(int index)
*/
void QTabBar::tabRemoved(int index)
{
- Q_UNUSED(index)
+ Q_UNUSED(index);
}
/*!
@@ -1594,6 +1654,8 @@ void QTabBar::showEvent(QShowEvent *)
d->refresh();
if (!d->validIndex(d->currentIndex))
setCurrentIndex(0);
+ else
+ d->makeVisible(d->currentIndex);
d->updateMacBorderMetrics();
}
@@ -1610,52 +1672,53 @@ void QTabBar::hideEvent(QHideEvent *)
bool QTabBar::event(QEvent *event)
{
Q_D(QTabBar);
- if (event->type() == QEvent::HoverMove
- || event->type() == QEvent::HoverEnter) {
+ switch (event->type()) {
+ case QEvent::HoverMove:
+ case QEvent::HoverEnter: {
QHoverEvent *he = static_cast<QHoverEvent *>(event);
- if (!d->hoverRect.contains(he->pos())) {
- QRect oldHoverRect = d->hoverRect;
- bool cursorOverTabs = false;
- for (int i = 0; i < d->tabList.count(); ++i) {
- QRect area = tabRect(i);
- if (area.contains(he->pos())) {
- d->hoverIndex = i;
- d->hoverRect = area;
- cursorOverTabs = true;
- break;
- }
- }
- if (!cursorOverTabs) {
- d->hoverIndex = -1;
+ d->mousePosition = he->position().toPoint();
+ if (!d->hoverRect.contains(d->mousePosition)) {
+ if (d->hoverRect.isValid())
+ update(d->hoverRect);
+ d->hoverIndex = tabAt(d->mousePosition);
+ if (d->validIndex(d->hoverIndex)) {
+ d->hoverRect = tabRect(d->hoverIndex);
+ update(d->hoverRect);
+ } else {
d->hoverRect = QRect();
}
- if (he->oldPos() != QPoint(-1, -1))
- update(oldHoverRect);
- update(d->hoverRect);
}
return true;
- } else if (event->type() == QEvent::HoverLeave) {
- QRect oldHoverRect = d->hoverRect;
+ }
+ case QEvent::HoverLeave: {
+ d->mousePosition = {-1, -1};
+ if (d->hoverRect.isValid())
+ update(d->hoverRect);
d->hoverIndex = -1;
d->hoverRect = QRect();
- update(oldHoverRect);
+#if QT_CONFIG(wheelevent)
+ d->accumulatedAngleDelta = QPoint();
+#endif
return true;
-#ifndef QT_NO_TOOLTIP
- } else if (event->type() == QEvent::ToolTip) {
+ }
+#if QT_CONFIG(tooltip)
+ case QEvent::ToolTip:
if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
if (!tab->toolTip.isEmpty()) {
QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
return true;
}
}
-#endif // QT_NO_TOOLTIP
+ break;
+#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(whatsthis)
- } else if (event->type() == QEvent::QueryWhatsThis) {
+ case QEvent::QEvent::QueryWhatsThis: {
const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
if (!tab || tab->whatsThis.isEmpty())
event->ignore();
return true;
- } else if (event->type() == QEvent::WhatsThis) {
+ }
+ case QEvent::WhatsThis:
if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
if (!tab->whatsThis.isEmpty()) {
QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
@@ -1663,35 +1726,34 @@ bool QTabBar::event(QEvent *event)
return true;
}
}
+ break;
#endif // QT_CONFIG(whatsthis)
#ifndef QT_NO_SHORTCUT
- } else if (event->type() == QEvent::Shortcut) {
+
+ case QEvent::Shortcut: {
QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
- for (int i = 0; i < d->tabList.count(); ++i) {
- const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
+ for (int i = 0; i < d->tabList.size(); ++i) {
+ const QTabBarPrivate::Tab *tab = d->tabList.at(i);
if (tab->shortcutId == se->shortcutId()) {
setCurrentIndex(i);
return true;
}
}
+ }
+ break;
#endif
- } else if (event->type() == QEvent::MouseButtonDblClick) { // ### fixme Qt 6: move to mouseDoubleClickEvent(), here for BC reasons.
- const QPoint pos = static_cast<const QMouseEvent *>(event)->pos();
- const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
- || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
- if (!isEventInCornerButtons)
- emit tabBarDoubleClicked(tabAt(pos));
- } else if (event->type() == QEvent::Move) {
+ case QEvent::Move:
d->updateMacBorderMetrics();
- return QWidget::event(event);
-
+ break;
#if QT_CONFIG(draganddrop)
- } else if (event->type() == QEvent::DragEnter) {
+
+ case QEvent::DragEnter:
if (d->changeCurrentOnDrag)
event->accept();
- } else if (event->type() == QEvent::DragMove) {
+ break;
+ case QEvent::DragMove:
if (d->changeCurrentOnDrag) {
- const int tabIndex = tabAt(static_cast<QDragMoveEvent *>(event)->pos());
+ const int tabIndex = tabAt(static_cast<QDragMoveEvent *>(event)->position().toPoint());
if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
d->switchTabCurrentIndex = tabIndex;
if (d->switchTabTimerId)
@@ -1700,11 +1762,23 @@ bool QTabBar::event(QEvent *event)
}
event->ignore();
}
- } else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) {
+ break;
+ case QEvent::DragLeave:
+ case QEvent::Drop:
d->killSwitchTabTimer();
event->ignore();
+ break;
#endif
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ d->mousePosition = static_cast<QMouseEvent *>(event)->position().toPoint();
+ d->mouseButtons = static_cast<QMouseEvent *>(event)->buttons();
+ break;
+ default:
+ break;
}
+
return QWidget::event(event);
}
@@ -1715,6 +1789,8 @@ void QTabBar::resizeEvent(QResizeEvent *)
Q_D(QTabBar);
if (d->layoutDirty)
updateGeometry();
+
+ // when resizing, we want to keep the scroll offset as much as possible
d->layoutTabs();
d->makeVisible(d->currentIndex);
@@ -1741,7 +1817,7 @@ void QTabBar::paintEvent(QPaintEvent *)
selected = d->pressedIndex;
const QRect scrollRect = d->normalizedScrollRect();
- for (int i = 0; i < d->tabList.count(); ++i)
+ for (int i = 0; i < d->tabList.size(); ++i)
optTabBase.tabBarRect |= tabRect(i);
optTabBase.selectedTabRect = tabRect(selected);
@@ -1749,64 +1825,91 @@ void QTabBar::paintEvent(QPaintEvent *)
if (d->drawBase)
p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
- for (int i = 0; i < d->tabList.count(); ++i) {
- QStyleOptionTab tab;
- initStyleOption(&tab, i);
- if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
+ // the buttons might be semi-transparent or not fill their rect, but we don't
+ // want the tab underneath to shine through, so clip the button area; QTBUG-50866
+ if (d->leftB->isVisible() || d->rightB->isVisible()) {
+ QStyleOption opt;
+ opt.initFrom(this);
+ QRegion buttonRegion;
+ if (d->leftB->isVisible())
+ buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, this);
+ if (d->rightB->isVisible())
+ buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, this);
+ if (!buttonRegion.isEmpty())
+ p.setClipRegion(QRegion(rect()) - buttonRegion);
+ }
+
+ for (int i = 0; i < d->tabList.size(); ++i) {
+ const auto tab = d->tabList.at(i);
+ if (!tab->visible)
+ continue;
+ QStyleOptionTab tabOption;
+ initStyleOption(&tabOption, i);
+ if (d->paintWithOffsets && tab->dragOffset != 0) {
if (vertical) {
- tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
+ tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
} else {
- tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
+ tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
}
}
- if (!(tab.state & QStyle::State_Enabled)) {
- tab.palette.setCurrentColorGroup(QPalette::Disabled);
+ if (!(tabOption.state & QStyle::State_Enabled)) {
+ tabOption.palette.setCurrentColorGroup(QPalette::Disabled);
}
// If this tab is partially obscured, make a note of it so that we can pass the information
// along when we draw the tear.
- QRect tabRect = d->tabList[i].rect;
+ QRect tabRect = tab->rect;
int tabStart = vertical ? tabRect.top() : tabRect.left();
int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
if (tabStart < scrollRect.left() + d->scrollOffset) {
cutLeft = i;
- cutTabLeft = tab;
+ cutTabLeft = tabOption;
} else if (tabEnd > scrollRect.right() + d->scrollOffset) {
cutRight = i;
- cutTabRight = tab;
+ cutTabRight = tabOption;
}
// Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
- if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
- || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
+ if ((!vertical && (tabOption.rect.right() < 0 || tabOption.rect.left() > width()))
+ || (vertical && (tabOption.rect.bottom() < 0 || tabOption.rect.top() > height())))
continue;
- optTabBase.tabBarRect |= tab.rect;
+ optTabBase.tabBarRect |= tabOption.rect;
if (i == selected)
continue;
- p.drawControl(QStyle::CE_TabBarTab, tab);
+ p.drawControl(QStyle::CE_TabBarTab, tabOption);
}
// Draw the selected tab last to get it "on top"
if (selected >= 0) {
- QStyleOptionTab tab;
- initStyleOption(&tab, selected);
- if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
+ QStyleOptionTab tabOption;
+ const auto tab = d->tabList.at(selected);
+ initStyleOption(&tabOption, selected);
+
+ if (d->paintWithOffsets && tab->dragOffset != 0) {
+ // if the drag offset is != 0, a move is in progress (drag or animation)
+ // => set the tab position to Moving to preserve the rect
+ tabOption.position = QStyleOptionTab::TabPosition::Moving;
+
if (vertical)
- tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
- else
- tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
- }
- if (!d->dragInProgress)
- p.drawControl(QStyle::CE_TabBarTab, tab);
- else {
- int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, this);
- if (verticalTabs(d->shape))
- d->movingTab->setGeometry(tab.rect.adjusted(0, -taboverlap, 0, taboverlap));
+ tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
else
- d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
+ tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
}
+
+ // Calculate the rect of a moving tab
+ const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
+ const QRect &movingRect = verticalTabs(d->shape)
+ ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
+ : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
+
+ // If a drag is in process, set the moving tab's geometry here
+ // (in an animation, it is already set)
+ if (d->dragInProgress)
+ d->movingTab->setGeometry(movingRect);
+
+ p.drawControl(QStyle::CE_TabBarTab, tabOption);
}
// Only draw the tear indicator if necessary. Most of the time we don't need too.
@@ -1824,6 +1927,63 @@ void QTabBar::paintEvent(QPaintEvent *)
}
/*
+ When index changes visibility, we have to find first & last visible indexes.
+ If remove is set, we force both
+ */
+void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool remove)
+{
+ if (visible) {
+ firstVisible = qMin(index, firstVisible);
+ lastVisible = qMax(index, lastVisible);
+ } else {
+ if (remove || (index == firstVisible)) {
+ firstVisible = -1;
+ for (int i = 0; i < tabList.size(); ++i) {
+ if (tabList.at(i)->visible) {
+ firstVisible = i;
+ break;
+ }
+ }
+ }
+ if (remove || (index == lastVisible)) {
+ lastVisible = -1;
+ for (int i = tabList.size() - 1; i >= 0; --i) {
+ if (tabList.at(i)->visible) {
+ lastVisible = i;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ Selects the new current index starting at "fromIndex". If "fromIndex" is visible we're done.
+ Else it tries any index AFTER fromIndex, then any BEFORE fromIndex and, if everything fails,
+ it returns -1 indicating that no index is available
+ */
+int QTabBarPrivate::selectNewCurrentIndexFrom(int fromIndex)
+{
+ int newindex = -1;
+ for (int i = fromIndex; i < tabList.size(); ++i) {
+ if (at(i)->visible && at(i)->enabled) {
+ newindex = i;
+ break;
+ }
+ }
+ if (newindex < 0) {
+ for (int i = fromIndex-1; i > -1; --i) {
+ if (at(i)->visible && at(i)->enabled) {
+ newindex = i;
+ break;
+ }
+ }
+ }
+
+ return newindex;
+}
+
+/*
Given that index at position from moved to position to where return where index goes.
*/
int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
@@ -1852,53 +2012,57 @@ void QTabBar::moveTab(int from, int to)
|| !d->validIndex(to))
return;
+ auto &fromTab = *d->tabList.at(from);
+ auto &toTab = *d->tabList.at(to);
+
bool vertical = verticalTabs(d->shape);
int oldPressedPosition = 0;
if (d->pressedIndex != -1) {
// Record the position of the pressed tab before reordering the tabs.
- oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
- : d->tabList[d->pressedIndex].rect.x();
+ oldPressedPosition = vertical ? d->tabList.at(d->pressedIndex)->rect.y()
+ : d->tabList.at(d->pressedIndex)->rect.x();
}
// Update the locations of the tabs first
int start = qMin(from, to);
int end = qMax(from, to);
- int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
+ int width = vertical ? fromTab.rect.height() : fromTab.rect.width();
if (from < to)
width *= -1;
bool rtl = isRightToLeft();
for (int i = start; i <= end; ++i) {
if (i == from)
continue;
+ auto &tab = *d->tabList.at(i);
if (vertical)
- d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
+ tab.rect.moveTop(tab.rect.y() + width);
else
- d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
+ tab.rect.moveLeft(tab.rect.x() + width);
int direction = -1;
if (rtl && !vertical)
direction *= -1;
- if (d->tabList[i].dragOffset != 0)
- d->tabList[i].dragOffset += (direction * width);
+ if (tab.dragOffset != 0)
+ tab.dragOffset += (direction * width);
}
if (vertical) {
if (from < to)
- d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
+ fromTab.rect.moveTop(toTab.rect.bottom() + 1);
else
- d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
+ fromTab.rect.moveTop(toTab.rect.top() - width);
} else {
if (from < to)
- d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
+ fromTab.rect.moveLeft(toTab.rect.right() + 1);
else
- d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
+ fromTab.rect.moveLeft(toTab.rect.left() - width);
}
// Move the actual data structures
d->tabList.move(from, to);
// update lastTab locations
- for (int i = 0; i < d->tabList.count(); ++i)
- d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
+ for (const auto tab : std::as_const(d->tabList))
+ tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
// update external variables
int previousIndex = d->currentIndex;
@@ -1907,7 +2071,8 @@ void QTabBar::moveTab(int from, int to)
// If we are in the middle of a drag update the dragStartPosition
if (d->pressedIndex != -1) {
d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
- int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
+ const auto pressedTab = d->tabList.at(d->pressedIndex);
+ int newPressedPosition = vertical ? pressedTab->rect.top() : pressedTab->rect.left();
int diff = oldPressedPosition - newPressedPosition;
if (isRightToLeft() && !vertical)
diff *= -1;
@@ -1939,15 +2104,15 @@ void QTabBarPrivate::slide(int from, int to)
q->setUpdatesEnabled(true);
int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
int length = postLocation - preLocation;
- tabList[to].dragOffset -= length;
- tabList[to].startAnimation(this, ANIMATION_DURATION);
+ tabList.at(to)->dragOffset -= length;
+ tabList.at(to)->startAnimation(this, ANIMATION_DURATION);
}
void QTabBarPrivate::moveTab(int index, int offset)
{
if (!validIndex(index))
return;
- tabList[index].dragOffset = offset;
+ tabList.at(index)->dragOffset = offset;
layoutTab(index); // Make buttons follow tab
q_func()->update();
}
@@ -1958,7 +2123,7 @@ void QTabBar::mousePressEvent(QMouseEvent *event)
{
Q_D(QTabBar);
- const QPoint pos = event->pos();
+ const QPoint pos = event->position().toPoint();
const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
|| (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
if (!isEventInCornerButtons) {
@@ -1974,20 +2139,18 @@ void QTabBar::mousePressEvent(QMouseEvent *event)
if (d->pressedIndex != -1 && d->movable)
d->moveTabFinished(d->pressedIndex);
- d->pressedIndex = d->indexAtPos(event->pos());
-#if 0 // Used to be included in Qt4 for Q_WS_MAC
- d->previousPressedIndex = d->pressedIndex;
-#endif
+ d->pressedIndex = d->indexAtPos(event->position().toPoint());
+
if (d->validIndex(d->pressedIndex)) {
QStyleOptionTabBarBase optTabBase;
- optTabBase.init(this);
+ optTabBase.initFrom(this);
optTabBase.documentMode = d->documentMode;
if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
setCurrentIndex(d->pressedIndex);
else
repaint(tabRect(d->pressedIndex));
if (d->movable) {
- d->dragStartPosition = event->pos();
+ d->dragStartPosition = event->position().toPoint();
}
}
}
@@ -2005,7 +2168,7 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event)
// Start drag
if (!d->dragInProgress && d->pressedIndex != -1) {
- if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
+ if ((event->position().toPoint() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
d->dragInProgress = true;
d->setupMovableTab();
}
@@ -2017,11 +2180,11 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event)
bool vertical = verticalTabs(d->shape);
int dragDistance;
if (vertical) {
- dragDistance = (event->pos().y() - d->dragStartPosition.y());
+ dragDistance = (event->position().toPoint().y() - d->dragStartPosition.y());
} else {
- dragDistance = (event->pos().x() - d->dragStartPosition.x());
+ dragDistance = (event->position().toPoint().x() - d->dragStartPosition.x());
}
- d->tabList[d->pressedIndex].dragOffset = dragDistance;
+ d->tabList.at(d->pressedIndex)->dragOffset = dragDistance;
QRect startingRect = tabRect(d->pressedIndex);
if (vertical)
@@ -2053,21 +2216,11 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event)
}
}
// Buttons needs to follow the dragged tab
- d->layoutTab(d->pressedIndex);
+ if (d->pressedIndex != -1)
+ d->layoutTab(d->pressedIndex);
update();
}
-#if 0 // Used to be included in Qt4 for Q_WS_MAC
- } else if (!d->documentMode && event->buttons() == Qt::LeftButton && d->previousPressedIndex != -1) {
- int newPressedIndex = d->indexAtPos(event->pos());
- if (d->pressedIndex == -1 && d->previousPressedIndex == newPressedIndex) {
- d->pressedIndex = d->previousPressedIndex;
- update(tabRect(d->pressedIndex));
- } else if(d->pressedIndex != newPressedIndex) {
- d->pressedIndex = -1;
- update(tabRect(d->previousPressedIndex));
- }
-#endif
}
if (event->buttons() != Qt::LeftButton) {
@@ -2082,22 +2235,21 @@ void QTabBarPrivate::setupMovableTab()
if (!movingTab)
movingTab = new QMovableTabWidget(q);
- int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0 ,q);
+ int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr ,q);
QRect grabRect = q->tabRect(pressedIndex);
if (verticalTabs(shape))
grabRect.adjust(0, -taboverlap, 0, taboverlap);
else
grabRect.adjust(-taboverlap, 0, taboverlap, 0);
- QPixmap grabImage(grabRect.size() * q->devicePixelRatioF());
- grabImage.setDevicePixelRatio(q->devicePixelRatioF());
+ QPixmap grabImage(grabRect.size() * q->devicePixelRatio());
+ grabImage.setDevicePixelRatio(q->devicePixelRatio());
grabImage.fill(Qt::transparent);
QStylePainter p(&grabImage, q);
- p.initFrom(q);
QStyleOptionTab tab;
q->initStyleOption(&tab, pressedIndex);
- tab.position = QStyleOptionTab::OnlyOneTab;
+ tab.position = QStyleOptionTab::Moving;
if (verticalTabs(shape))
tab.rect.moveTopLeft(QPoint(0, taboverlap));
else
@@ -2110,10 +2262,11 @@ void QTabBarPrivate::setupMovableTab()
movingTab->raise();
// Re-arrange widget order to avoid overlaps
- if (tabList[pressedIndex].leftWidget)
- tabList[pressedIndex].leftWidget->raise();
- if (tabList[pressedIndex].rightWidget)
- tabList[pressedIndex].rightWidget->raise();
+ const auto &pressedTab = *tabList.at(pressedIndex);
+ if (pressedTab.leftWidget)
+ pressedTab.leftWidget->raise();
+ if (pressedTab.rightWidget)
+ pressedTab.rightWidget->raise();
if (leftB)
leftB->raise();
if (rightB)
@@ -2127,17 +2280,18 @@ void QTabBarPrivate::moveTabFinished(int index)
bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
bool allAnimationsFinished = true;
#if QT_CONFIG(animation)
- for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
- const Tab &t = tabList.at(i);
- if (t.animation && t.animation->state() == QAbstractAnimation::Running)
+ for (const auto tab : std::as_const(tabList)) {
+ if (tab->animation && tab->animation->state() == QAbstractAnimation::Running) {
allAnimationsFinished = false;
+ break;
+ }
}
#endif // animation
if (allAnimationsFinished && cleanup) {
- if(movingTab)
+ if (movingTab)
movingTab->setVisible(false); // We might not get a mouse release
- for (int i = 0; i < tabList.count(); ++i) {
- tabList[i].dragOffset = 0;
+ for (auto tab : std::as_const(tabList)) {
+ tab->dragOffset = 0;
}
if (pressedIndex != -1 && movable) {
pressedIndex = -1;
@@ -2148,7 +2302,7 @@ void QTabBarPrivate::moveTabFinished(int index)
} else {
if (!validIndex(index))
return;
- tabList[index].dragOffset = 0;
+ tabList.at(index)->dragOffset = 0;
}
q->update();
}
@@ -2162,23 +2316,23 @@ void QTabBar::mouseReleaseEvent(QMouseEvent *event)
event->ignore();
return;
}
-#if 0 // Used to be included in Qt4 for Q_WS_MAC
- d->previousPressedIndex = -1;
-#endif
+
if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
- int length = d->tabList[d->pressedIndex].dragOffset;
+ int length = d->tabList.at(d->pressedIndex)->dragOffset;
int width = verticalTabs(d->shape)
? tabRect(d->pressedIndex).height()
: tabRect(d->pressedIndex).width();
int duration = qMin(ANIMATION_DURATION,
(qAbs(length) * ANIMATION_DURATION) / width);
- d->tabList[d->pressedIndex].startAnimation(d, duration);
+ d->tabList.at(d->pressedIndex)->startAnimation(d, duration);
d->dragInProgress = false;
d->movingTab->setVisible(false);
d->dragStartPosition = QPoint();
}
- int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
+ // mouse release event might happen outside the tab, so keep the pressed index
+ int oldPressedIndex = d->pressedIndex;
+ int i = d->indexAtPos(event->position().toPoint()) == d->pressedIndex ? d->pressedIndex : -1;
d->pressedIndex = -1;
QStyleOptionTabBarBase optTabBase;
optTabBase.initFrom(this);
@@ -2187,8 +2341,22 @@ void QTabBar::mouseReleaseEvent(QMouseEvent *event)
(style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease);
if (selectOnRelease)
setCurrentIndex(i);
- if (!selectOnRelease || !d->validIndex(i) || d->currentIndex == i)
- repaint(tabRect(i));
+ if (d->validIndex(oldPressedIndex))
+ update(tabRect(oldPressedIndex));
+}
+
+/*!\reimp
+ */
+void QTabBar::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QTabBar);
+ const QPoint pos = event->position().toPoint();
+ const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
+ || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
+ if (!isEventInCornerButtons)
+ emit tabBarDoubleClicked(tabAt(pos));
+
+ mousePressEvent(event);
}
/*!\reimp
@@ -2209,14 +2377,61 @@ void QTabBar::keyPressEvent(QKeyEvent *event)
#if QT_CONFIG(wheelevent)
void QTabBar::wheelEvent(QWheelEvent *event)
{
-#ifndef Q_OS_MAC
Q_D(QTabBar);
- int offset = event->delta() > 0 ? -1 : 1;
- d->setCurrentNextEnabledIndex(offset);
- QWidget::wheelEvent(event);
-#else
- Q_UNUSED(event)
-#endif
+ if (style()->styleHint(QStyle::SH_TabBar_AllowWheelScrolling)) {
+ const bool wheelVertical = qAbs(event->angleDelta().y()) > qAbs(event->angleDelta().x());
+ const bool tabsVertical = verticalTabs(d->shape);
+ if (event->device()->capabilities().testFlag(QInputDevice::Capability::PixelScroll)) {
+ // For wheels/touch pads with pixel precision, scroll the tab bar if
+ // it has the right orientation.
+ int delta = 0;
+ if (tabsVertical == wheelVertical)
+ delta = wheelVertical ? event->pixelDelta().y() : event->pixelDelta().x();
+ if (layoutDirection() == Qt::RightToLeft)
+ delta = -delta;
+ if (delta && d->validIndex(d->lastVisible)) {
+ const int oldScrollOffset = d->scrollOffset;
+ const QRect lastTabRect = d->tabList.at(d->lastVisible)->rect;
+ const QRect scrollRect = d->normalizedScrollRect(d->lastVisible);
+ int scrollRectExtent = scrollRect.right();
+ if (!d->leftB->isVisible())
+ scrollRectExtent += tabsVertical ? d->leftB->height() : d->leftB->width();
+ if (!d->rightB->isVisible())
+ scrollRectExtent += tabsVertical ? d->rightB->height() : d->rightB->width();
+
+ const int maxScrollOffset = qMax((tabsVertical ?
+ lastTabRect.bottom() :
+ lastTabRect.right()) - scrollRectExtent, 0);
+ d->scrollOffset = qBound(0, d->scrollOffset - delta, maxScrollOffset);
+ d->leftB->setEnabled(d->scrollOffset > -scrollRect.left());
+ d->rightB->setEnabled(maxScrollOffset > d->scrollOffset);
+ if (oldScrollOffset != d->scrollOffset) {
+ event->accept();
+ update();
+ return;
+ }
+ }
+ } else {
+ d->accumulatedAngleDelta += event->angleDelta();
+ const int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
+ const int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
+ int offset = 0;
+ if (xSteps > 0 || ySteps > 0) {
+ offset = -1;
+ d->accumulatedAngleDelta = QPoint();
+ } else if (xSteps < 0 || ySteps < 0) {
+ offset = 1;
+ d->accumulatedAngleDelta = QPoint();
+ }
+ const int oldCurrentIndex = d->currentIndex;
+ d->setCurrentNextEnabledIndex(offset);
+ if (oldCurrentIndex != d->currentIndex) {
+ event->accept();
+ return;
+ }
+ }
+ QWidget::wheelEvent(event);
+ }
}
#endif // QT_CONFIG(wheelevent)
@@ -2224,7 +2439,7 @@ void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
{
Q_Q(QTabBar);
for (int index = currentIndex + offset; validIndex(index); index += offset) {
- if (tabList.at(index).enabled) {
+ if (tabList.at(index)->enabled && tabList.at(index)->visible) {
q->setCurrentIndex(index);
break;
}
@@ -2239,9 +2454,9 @@ void QTabBar::changeEvent(QEvent *event)
switch (event->type()) {
case QEvent::StyleChange:
if (!d->elideModeSetByUser)
- d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
+ d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, nullptr, this));
if (!d->useScrollButtonsSetByUser)
- d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
+ d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, nullptr, this);
Q_FALLTHROUGH();
case QEvent::FontChange:
d->textSizes.clear();
@@ -2277,7 +2492,7 @@ void QTabBar::timerEvent(QTimerEvent *event)
This property controls how items are elided when there is not
enough space to show them for a given tab bar size.
- By default the value is style dependent.
+ By default the value is style-dependent.
\sa QTabWidget::elideMode, usesScrollButtons, QStyle::SH_TabBar_ElideMode
*/
@@ -2306,7 +2521,7 @@ void QTabBar::setElideMode(Qt::TextElideMode mode)
When there are too many tabs in a tab bar for its size, the tab bar can either choose
to expand its size or to add buttons that allow you to scroll through the tabs.
- By default the value is style dependant.
+ By default the value is style-dependent.
\sa elideMode, QTabWidget::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
*/
@@ -2351,26 +2566,27 @@ void QTabBar::setTabsClosable(bool closable)
if (d->closeButtonOnTabs == closable)
return;
d->closeButtonOnTabs = closable;
- ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
+ ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
if (!closable) {
- for (int i = 0; i < d->tabList.count(); ++i) {
- if (closeSide == LeftSide && d->tabList[i].leftWidget) {
- d->tabList[i].leftWidget->deleteLater();
- d->tabList[i].leftWidget = 0;
+ for (auto tab : std::as_const(d->tabList)) {
+ if (closeSide == LeftSide && tab->leftWidget) {
+ tab->leftWidget->deleteLater();
+ tab->leftWidget = nullptr;
}
- if (closeSide == RightSide && d->tabList[i].rightWidget) {
- d->tabList[i].rightWidget->deleteLater();
- d->tabList[i].rightWidget = 0;
+ if (closeSide == RightSide && tab->rightWidget) {
+ tab->rightWidget->deleteLater();
+ tab->rightWidget = nullptr;
}
}
} else {
bool newButtons = false;
- for (int i = 0; i < d->tabList.count(); ++i) {
+ for (int i = 0; i < d->tabList.size(); ++i) {
if (tabButton(i, closeSide))
continue;
newButtons = true;
QAbstractButton *closeButton = new CloseButton(this);
- connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ QObjectPrivate::connect(closeButton, &CloseButton::clicked,
+ d, &QTabBarPrivate::closeTab);
setTabButton(i, closeSide, closeButton);
}
if (newButtons)
@@ -2560,21 +2776,22 @@ void QTabBar::setChangeCurrentOnDrag(bool change)
/*!
Sets \a widget on the tab \a index. The widget is placed
- on the left or right hand side depending upon the \a position.
+ on the left or right hand side depending on the \a position.
\since 4.5
- Any previously set widget in \a position is hidden.
+ Any previously set widget in \a position is hidden. Setting \a widget
+ to \nullptr will hide the current widget at \a position.
The tab bar will take ownership of the widget and so all widgets set here
will be deleted by the tab bar when it is destroyed unless you separately
- reparent the widget after setting some other widget (or 0).
+ reparent the widget after setting some other widget (or \nullptr).
\sa tabsClosable()
*/
void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
{
Q_D(QTabBar);
- if (index < 0 || index >= d->tabList.count())
+ if (index < 0 || index >= d->tabList.size())
return;
if (widget) {
widget->setParent(this);
@@ -2582,14 +2799,15 @@ void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
widget->lower();
widget->show();
}
+ auto &tab = *d->tabList.at(index);
if (position == LeftSide) {
- if (d->tabList[index].leftWidget)
- d->tabList[index].leftWidget->hide();
- d->tabList[index].leftWidget = widget;
+ if (tab.leftWidget)
+ tab.leftWidget->hide();
+ tab.leftWidget = widget;
} else {
- if (d->tabList[index].rightWidget)
- d->tabList[index].rightWidget->hide();
- d->tabList[index].rightWidget = widget;
+ if (tab.rightWidget)
+ tab.rightWidget->hide();
+ tab.rightWidget = widget;
}
d->layoutTabs();
d->refresh();
@@ -2597,21 +2815,20 @@ void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
}
/*!
- Returns the widget set a tab \a index and \a position or 0 if
- one is not set.
+ Returns the widget set a tab \a index and \a position or \nullptr
+ if one is not set.
*/
QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
{
Q_D(const QTabBar);
- if (index < 0 || index >= d->tabList.count())
- return 0;
- if (position == LeftSide)
- return d->tabList.at(index).leftWidget;
- else
- return d->tabList.at(index).rightWidget;
+ if (const auto tab = d->at(index)) {
+ return position == LeftSide ? tab->leftWidget
+ : tab->rightWidget;
+ }
+ return nullptr;
}
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
/*!
Sets the accessibleName of the tab at position \a index to \a name.
*/
@@ -2637,7 +2854,7 @@ QString QTabBar::accessibleTabName(int index) const
return tab->accessibleName;
return QString();
}
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
CloseButton::CloseButton(QWidget *parent)
: QAbstractButton(parent)
@@ -2646,7 +2863,7 @@ CloseButton::CloseButton(QWidget *parent)
#ifndef QT_NO_CURSOR
setCursor(Qt::ArrowCursor);
#endif
-#ifndef QT_NO_TOOLTIP
+#if QT_CONFIG(tooltip)
setToolTip(tr("Close Tab"));
#endif
resize(sizeHint());
@@ -2655,12 +2872,12 @@ CloseButton::CloseButton(QWidget *parent)
QSize CloseButton::sizeHint() const
{
ensurePolished();
- int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
- int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
+ int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, nullptr, this);
+ int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, nullptr, this);
return QSize(width, height);
}
-void CloseButton::enterEvent(QEvent *event)
+void CloseButton::enterEvent(QEnterEvent *event)
{
if (isEnabled())
update();
@@ -2678,7 +2895,7 @@ void CloseButton::paintEvent(QPaintEvent *)
{
QPainter p(this);
QStyleOption opt;
- opt.init(this);
+ opt.initFrom(this);
opt.state |= QStyle::State_AutoRaise;
if (isEnabled() && underMouse() && !isChecked() && !isDown())
opt.state |= QStyle::State_Raised;
@@ -2689,7 +2906,7 @@ void CloseButton::paintEvent(QPaintEvent *)
if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
int index = tb->currentIndex();
- QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
+ QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, tb);
if (tb->tabButton(index, position) == this)
opt.state |= QStyle::State_Selected;
}
@@ -2700,12 +2917,12 @@ void CloseButton::paintEvent(QPaintEvent *)
#if QT_CONFIG(animation)
void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(const QVariant &current)
{
- priv->moveTab(priv->tabList.indexOf(*tab), current.toInt());
+ priv->moveTab(priv->tabList.indexOf(tab), current.toInt());
}
void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
{
- if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(*tab));
+ if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(tab));
}
#endif