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.cpp356
1 files changed, 200 insertions, 156 deletions
diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp
index 2738afcbdc..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"
@@ -59,7 +21,7 @@
#include "qwhatsthis.h"
#endif
#include "private/qtextengine_p.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
#include "qaccessible.h"
#endif
#ifdef Q_OS_MACOS
@@ -72,6 +34,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace {
class CloseButton : public QAbstractButton
{
@@ -107,14 +71,6 @@ 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_MACOS)
@@ -319,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
@@ -408,14 +364,16 @@ void QTabBarPrivate::init()
{
Q_Q(QTabBar);
leftB = new QToolButton(q);
- leftB->setObjectName(u"ScrollLeftButton"_qs);
+ 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"_qs);
+ 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 (QApplicationPrivate::keypadNavigationEnabled()) {
@@ -426,7 +384,7 @@ 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
@@ -440,7 +398,7 @@ 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)
+ for (int i = 0; i < tabList.size(); ++i)
if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
return i;
return -1;
@@ -458,7 +416,7 @@ void QTabBarPrivate::layoutTabs()
int hiddenTabs = 0;
Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, nullptr, q));
- QList<QLayoutStruct> tabChain(tabList.count() + 2);
+ 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.
@@ -481,7 +439,7 @@ void QTabBarPrivate::layoutTabs()
int minx = 0;
int x = 0;
int maxHeight = 0;
- for (int i = 0; i < tabList.count(); ++i) {
+ for (int i = 0; i < tabList.size(); ++i) {
const auto tab = tabList.at(i);
if (!tab->visible) {
++hiddenTabs;
@@ -512,7 +470,7 @@ void QTabBarPrivate::layoutTabs()
int miny = 0;
int y = 0;
int maxWidth = 0;
- for (int i = 0; i < tabList.count(); ++i) {
+ for (int i = 0; i < tabList.size(); ++i) {
auto tab = tabList.at(i);
if (!tab->visible) {
++hiddenTabs;
@@ -523,7 +481,7 @@ void QTabBarPrivate::layoutTabs()
y += sz.height();
maxWidth = qMax(maxWidth, sz.width());
sz = q->minimumTabSizeHint(i);
- tab->data = 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 = tab->maxRect.height();
@@ -547,14 +505,14 @@ void QTabBarPrivate::layoutTabs()
&& (tabAlignment != Qt::AlignRight)
&& (tabAlignment != Qt::AlignJustify);
tabChain[tabChainIndex].empty = true;
- Q_ASSERT(tabChainIndex == tabChain.count() - 1 - hiddenTabs); // 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
hiddenTabs = 0;
- for (int i = 0; i < tabList.count(); ++i) {
+ for (int i = 0; i < tabList.size(); ++i) {
auto tab = tabList.at(i);
if (!tab->visible) {
tab->rect = QRect();
@@ -568,9 +526,8 @@ void QTabBarPrivate::layoutTabs()
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;
@@ -607,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();
}
@@ -627,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();
@@ -706,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 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 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());
@@ -798,13 +767,13 @@ 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, nullptr, q);
- for (int i = 0; i < tabList.count(); ++i) {
+ for (int i = 0; i < tabList.size(); ++i) {
if (closeSide == QTabBar::LeftSide) {
if (tabList.at(i)->leftWidget == object) {
tabToClose = i;
@@ -821,7 +790,7 @@ void QTabBarPrivate::_q_closeTab()
emit q->tabCloseRequested(tabToClose);
}
-void QTabBarPrivate::_q_scrollTabs()
+void QTabBarPrivate::scrollTabs()
{
Q_Q(QTabBar);
const QObject *sender = q->sender();
@@ -831,7 +800,7 @@ void QTabBarPrivate::_q_scrollTabs()
int i = -1;
if (sender == leftB) {
- for (i = tabList.count() - 1; i >= 0; --i) {
+ 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);
@@ -839,7 +808,7 @@ void QTabBarPrivate::_q_scrollTabs()
}
}
} else if (sender == rightB) {
- for (i = 0; i < tabList.count(); ++i) {
+ 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();
@@ -962,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)
@@ -986,7 +955,7 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
{
Q_D(QTabBar);
if (!d->validIndex(index)) {
- index = d->tabList.count();
+ index = d->tabList.size();
d->tabList.append(new QTabBarPrivate::Tab(icon, text));
} else {
d->tabList.insert(index, new QTabBarPrivate::Tab(icon, text));
@@ -996,7 +965,7 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &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;
@@ -1011,15 +980,21 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
initStyleOption(&opt, index);
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 (const auto tab : qAsConst(d->tabList)) {
+ 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);
d->autoHideTabs();
return index;
@@ -1056,7 +1031,7 @@ void QTabBar::removeTab(int index)
int newIndex = removedTab->lastTab;
d->tabList.removeAt(index);
delete removedTab;
- for (auto tab : qAsConst(d->tabList)) {
+ for (auto tab : std::as_const(d->tabList)) {
if (tab->lastTab == index)
tab->lastTab = -1;
if (tab->lastTab > index)
@@ -1083,8 +1058,6 @@ void QTabBar::removeTab(int index)
break;
case SelectLeftTab:
newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
- if (newIndex < 0)
- newIndex = 0;
break;
default:
break;
@@ -1095,6 +1068,9 @@ void QTabBar::removeTab(int index)
int bump = d->tabList.at(newIndex)->lastTab;
setCurrentIndex(newIndex);
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);
@@ -1104,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);
}
@@ -1433,13 +1408,24 @@ void QTabBar::setCurrentIndex(int index)
int oldIndex = d->currentIndex;
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);
- tab->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);
@@ -1492,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();
}
@@ -1535,10 +1521,10 @@ 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:
@@ -1668,6 +1654,8 @@ void QTabBar::showEvent(QShowEvent *)
d->refresh();
if (!d->validIndex(d->currentIndex))
setCurrentIndex(0);
+ else
+ d->makeVisible(d->currentIndex);
d->updateMacBorderMetrics();
}
@@ -1688,33 +1676,29 @@ bool QTabBar::event(QEvent *event)
case QEvent::HoverMove:
case QEvent::HoverEnter: {
QHoverEvent *he = static_cast<QHoverEvent *>(event);
- if (!d->hoverRect.contains(he->position().toPoint())) {
- QRect oldHoverRect = d->hoverRect;
- bool cursorOverTabs = false;
- for (int i = 0; i < d->tabList.count(); ++i) {
- QRect area = tabRect(i);
- if (area.contains(he->position().toPoint())) {
- 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;
}
case QEvent::HoverLeave: {
- QRect oldHoverRect = d->hoverRect;
+ 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;
}
#if QT_CONFIG(tooltip)
@@ -1748,7 +1732,7 @@ bool QTabBar::event(QEvent *event)
case QEvent::Shortcut: {
QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
- for (int i = 0; i < d->tabList.count(); ++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);
@@ -1788,6 +1772,7 @@ bool QTabBar::event(QEvent *event)
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:
@@ -1804,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);
@@ -1830,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);
@@ -1852,7 +1839,7 @@ void QTabBar::paintEvent(QPaintEvent *)
p.setClipRegion(QRegion(rect()) - buttonRegion);
}
- for (int i = 0; i < d->tabList.count(); ++i) {
+ for (int i = 0; i < d->tabList.size(); ++i) {
const auto tab = d->tabList.at(i);
if (!tab->visible)
continue;
@@ -1899,21 +1886,30 @@ void QTabBar::paintEvent(QPaintEvent *)
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)
tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
else
tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
}
- if (!d->dragInProgress)
- p.drawControl(QStyle::CE_TabBarTab, tabOption);
- else {
- int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
- if (verticalTabs(d->shape))
- d->movingTab->setGeometry(tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap));
- else
- d->movingTab->setGeometry(tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0));
- }
+
+ // 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.
@@ -1942,18 +1938,16 @@ void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool rem
} else {
if (remove || (index == firstVisible)) {
firstVisible = -1;
- for (int i = 0; i < tabList.count(); ++i) {
+ for (int i = 0; i < tabList.size(); ++i) {
if (tabList.at(i)->visible) {
firstVisible = i;
break;
}
}
- if (firstVisible < 0)
- firstVisible = 0;
}
if (remove || (index == lastVisible)) {
lastVisible = -1;
- for (int i = tabList.count() - 1; i >= 0; --i) {
+ for (int i = tabList.size() - 1; i >= 0; --i) {
if (tabList.at(i)->visible) {
lastVisible = i;
break;
@@ -1971,7 +1965,7 @@ void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool rem
int QTabBarPrivate::selectNewCurrentIndexFrom(int fromIndex)
{
int newindex = -1;
- for (int i = fromIndex; i < tabList.count(); ++i) {
+ for (int i = fromIndex; i < tabList.size(); ++i) {
if (at(i)->visible && at(i)->enabled) {
newindex = i;
break;
@@ -2067,7 +2061,7 @@ void QTabBar::moveTab(int from, int to)
d->tabList.move(from, to);
// update lastTab locations
- for (const auto tab : qAsConst(d->tabList))
+ for (const auto tab : std::as_const(d->tabList))
tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
// update external variables
@@ -2222,7 +2216,8 @@ 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();
}
@@ -2254,7 +2249,7 @@ void QTabBarPrivate::setupMovableTab()
QStyleOptionTab tab;
q->initStyleOption(&tab, pressedIndex);
- tab.position = QStyleOptionTab::OnlyOneTab;
+ tab.position = QStyleOptionTab::Moving;
if (verticalTabs(shape))
tab.rect.moveTopLeft(QPoint(0, taboverlap));
else
@@ -2285,7 +2280,7 @@ void QTabBarPrivate::moveTabFinished(int index)
bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
bool allAnimationsFinished = true;
#if QT_CONFIG(animation)
- for (const auto tab : qAsConst(tabList)) {
+ for (const auto tab : std::as_const(tabList)) {
if (tab->animation && tab->animation->state() == QAbstractAnimation::Running) {
allAnimationsFinished = false;
break;
@@ -2295,7 +2290,7 @@ void QTabBarPrivate::moveTabFinished(int index)
if (allAnimationsFinished && cleanup) {
if (movingTab)
movingTab->setVisible(false); // We might not get a mouse release
- for (auto tab : qAsConst(tabList)) {
+ for (auto tab : std::as_const(tabList)) {
tab->dragOffset = 0;
}
if (pressedIndex != -1 && movable) {
@@ -2384,10 +2379,57 @@ void QTabBar::wheelEvent(QWheelEvent *event)
{
Q_D(QTabBar);
if (style()->styleHint(QStyle::SH_TabBar_AllowWheelScrolling)) {
- int delta = (qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y()) ?
- event->angleDelta().x() : event->angleDelta().y());
- int offset = delta > 0 ? -1 : 1;
- d->setCurrentNextEnabledIndex(offset);
+ 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);
}
}
@@ -2397,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;
}
@@ -2450,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
*/
@@ -2479,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
*/
@@ -2526,7 +2568,7 @@ void QTabBar::setTabsClosable(bool closable)
d->closeButtonOnTabs = closable;
ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
if (!closable) {
- for (auto tab : qAsConst(d->tabList)) {
+ for (auto tab : std::as_const(d->tabList)) {
if (closeSide == LeftSide && tab->leftWidget) {
tab->leftWidget->deleteLater();
tab->leftWidget = nullptr;
@@ -2538,12 +2580,13 @@ void QTabBar::setTabsClosable(bool closable)
}
} 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)
@@ -2733,10 +2776,11 @@ 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
@@ -2747,7 +2791,7 @@ void QTabBar::setChangeCurrentOnDrag(bool change)
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);
@@ -2784,7 +2828,7 @@ QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
return nullptr;
}
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
/*!
Sets the accessibleName of the tab at position \a index to \a name.
*/
@@ -2810,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)