Refactor QTabBarPrivate::Tab and much of its usage in QTabBar
The type was broken in that it pretends to be a value, but didn't implement the necessary operators for save copy, move, or comparison. Tabs are supposed to be managed by Tab instance, as the crazy implementation of operator== demonstrated. Refactor the code to use it as a pointer consistently, even though this means additional allocations for each tab. This is however acceptable since there are not millions of tabs, and there is only a single place where tabs are removed from the list. Also use ranged for where possible, and never access the tabList using operator[] to avoid detaches. Use a std::unique_ptr for the animation in the tab, which implicitly protects against attempts to copy a Tab, and allows us to use the compiler generated default destructor. Add Q_DISABLE_COPY_MOVE for good measure, the compiler-generated move semantics would not work either due to the back-pointer from animation to the tab. Change-Id: I8e8c071472f8f1f401b0f4f1dde074d800842934 Fixes: QTBUG-85394 Reviewed-by: Lars Knoll <lars.knoll@qt.io>bb10
parent
652459afde
commit
359616066e
|
|
@ -174,7 +174,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);
|
||||
|
|
@ -433,23 +433,13 @@ void QTabBarPrivate::init()
|
|||
useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, nullptr, q);
|
||||
}
|
||||
|
||||
QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
|
||||
{
|
||||
return validIndex(index)?&tabList[index]:nullptr;
|
||||
}
|
||||
|
||||
const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
|
||||
{
|
||||
return validIndex(index)?&tabList[index]:nullptr;
|
||||
}
|
||||
|
||||
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))
|
||||
if (tabList.at(i)->enabled && q->tabRect(i).contains(p))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -461,7 +451,6 @@ void QTabBarPrivate::layoutTabs()
|
|||
QSize size = q->size();
|
||||
int last, available;
|
||||
int maxExtent;
|
||||
int i;
|
||||
bool vertTabs = verticalTabs(shape);
|
||||
int tabChainIndex = 0;
|
||||
int hiddenTabs = 0;
|
||||
|
|
@ -490,20 +479,21 @@ void QTabBarPrivate::layoutTabs()
|
|||
int minx = 0;
|
||||
int x = 0;
|
||||
int maxHeight = 0;
|
||||
for (i = 0; i < tabList.count(); ++i) {
|
||||
if (!tabList.at(i).visible) {
|
||||
for (int i = 0; i < tabList.count(); ++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;
|
||||
|
|
@ -520,20 +510,21 @@ void QTabBarPrivate::layoutTabs()
|
|||
int miny = 0;
|
||||
int y = 0;
|
||||
int maxWidth = 0;
|
||||
for (i = 0; i < tabList.count(); ++i) {
|
||||
if (!tabList.at(i).visible) {
|
||||
for (int i = 0; i < tabList.count(); ++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->data = 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;
|
||||
|
|
@ -561,17 +552,18 @@ void QTabBarPrivate::layoutTabs()
|
|||
|
||||
// Use the results
|
||||
hiddenTabs = 0;
|
||||
for (i = 0; i < tabList.count(); ++i) {
|
||||
if (!tabList.at(i).visible) {
|
||||
tabList[i].rect = QRect();
|
||||
for (int i = 0; i < tabList.count(); ++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) {
|
||||
|
|
@ -659,7 +651,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())
|
||||
|
|
@ -690,7 +682,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())
|
||||
|
|
@ -715,12 +707,12 @@ void QTabBarPrivate::makeVisible(int index)
|
|||
if (!validIndex(index) || leftB->isHidden())
|
||||
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 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 int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
|
||||
const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
|
||||
|
|
@ -757,34 +749,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -812,12 +804,12 @@ void QTabBarPrivate::_q_closeTab()
|
|||
QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, q);
|
||||
for (int i = 0; i < tabList.count(); ++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;
|
||||
}
|
||||
|
|
@ -837,7 +829,7 @@ void QTabBarPrivate::_q_scrollTabs()
|
|||
|
||||
if (sender == leftB) {
|
||||
for (i = tabList.count() - 1; i >= 0; --i) {
|
||||
int start = horizontal ? tabList.at(i).rect.left() : tabList.at(i).rect.top();
|
||||
int start = horizontal ? tabList.at(i)->rect.left() : tabList.at(i)->rect.top();
|
||||
if (start < scrollRect.left() + scrollOffset) {
|
||||
makeVisible(i);
|
||||
return;
|
||||
|
|
@ -845,7 +837,7 @@ void QTabBarPrivate::_q_scrollTabs()
|
|||
}
|
||||
} else if (sender == rightB) {
|
||||
for (i = 0; i < tabList.count(); ++i) {
|
||||
int end = horizontal ? tabList.at(i).rect.right() : tabList.at(i).rect.bottom();
|
||||
int end = horizontal ? tabList.at(i)->rect.right() : tabList.at(i)->rect.bottom();
|
||||
if (end > scrollRect.right() + scrollOffset) {
|
||||
makeVisible(i);
|
||||
return;
|
||||
|
|
@ -990,12 +982,12 @@ 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));
|
||||
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->lastVisible = qMax(index, d->lastVisible);
|
||||
|
|
@ -1016,9 +1008,9 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
|
|||
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 : qAsConst(d->tabList)) {
|
||||
if (tab->lastTab >= index)
|
||||
++tab->lastTab;
|
||||
}
|
||||
|
||||
tabInserted(index);
|
||||
|
|
@ -1036,30 +1028,32 @@ 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 = nullptr;
|
||||
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 = nullptr;
|
||||
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 : qAsConst(d->tabList)) {
|
||||
if (tab->lastTab == index)
|
||||
tab->lastTab = -1;
|
||||
if (tab->lastTab > index)
|
||||
--tab->lastTab;
|
||||
}
|
||||
|
||||
d->calculateFirstLastVisible(index, false, true);
|
||||
|
|
@ -1074,7 +1068,7 @@ void QTabBar::removeTab(int index)
|
|||
case SelectPreviousTab:
|
||||
if (newIndex > index)
|
||||
newIndex--;
|
||||
if (d->validIndex(newIndex) && d->tabList.at(newIndex).visible)
|
||||
if (d->validIndex(newIndex) && d->tabList.at(newIndex)->visible)
|
||||
break;
|
||||
Q_FALLTHROUGH();
|
||||
case SelectRightTab:
|
||||
|
|
@ -1091,9 +1085,9 @@ 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 {
|
||||
emit currentChanged(-1);
|
||||
|
|
@ -1161,7 +1155,7 @@ bool QTabBar::isTabVisible(int index) const
|
|||
{
|
||||
Q_D(const QTabBar);
|
||||
if (d->validIndex(index))
|
||||
return d->tabList.at(index).visible;
|
||||
return d->tabList.at(index)->visible;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1426,13 +1420,15 @@ 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;
|
||||
update();
|
||||
d->makeVisible(index);
|
||||
d->tabList[index].lastTab = oldIndex;
|
||||
tab->lastTab = oldIndex;
|
||||
if (oldIndex >= 0 && oldIndex < count())
|
||||
d->layoutTab(oldIndex);
|
||||
d->layoutTab(index);
|
||||
|
|
@ -1501,9 +1497,9 @@ QSize QTabBar::sizeHint() const
|
|||
if (d->layoutDirty)
|
||||
const_cast<QTabBarPrivate*>(d)->layoutTabs();
|
||||
QRect r;
|
||||
for (int i = 0; i < d->tabList.count(); ++i) {
|
||||
if (d->tabList.at(i).visible)
|
||||
r = r.united(d->tabList.at(i).maxRect);
|
||||
for (const auto tab : d->tabList) {
|
||||
if (tab->visible)
|
||||
r = r.united(tab->maxRect);
|
||||
}
|
||||
return r.size();
|
||||
}
|
||||
|
|
@ -1517,9 +1513,9 @@ QSize QTabBar::minimumSizeHint() const
|
|||
const_cast<QTabBarPrivate*>(d)->layoutTabs();
|
||||
if (!d->useScrollButtons) {
|
||||
QRect r;
|
||||
for (int i = 0; i < d->tabList.count(); ++i) {
|
||||
if (d->tabList.at(i).visible)
|
||||
r = r.united(d->tabList.at(i).minRect);
|
||||
for (const auto tab : d->tabList) {
|
||||
if (tab->visible)
|
||||
r = r.united(tab->minRect);
|
||||
}
|
||||
return r.size();
|
||||
}
|
||||
|
|
@ -1562,11 +1558,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;
|
||||
}
|
||||
|
||||
|
|
@ -1580,7 +1576,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);
|
||||
|
|
@ -1746,7 +1742,7 @@ bool QTabBar::event(QEvent *event)
|
|||
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);
|
||||
const QTabBarPrivate::Tab *tab = d->tabList.at(i);
|
||||
if (tab->shortcutId == se->shortcutId()) {
|
||||
setCurrentIndex(i);
|
||||
return true;
|
||||
|
|
@ -1836,64 +1832,66 @@ void QTabBar::paintEvent(QPaintEvent *)
|
|||
p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
|
||||
|
||||
for (int i = 0; i < d->tabList.count(); ++i) {
|
||||
if (!d->at(i)->visible)
|
||||
const auto tab = d->tabList.at(i);
|
||||
if (!tab->visible)
|
||||
continue;
|
||||
QStyleOptionTab tab;
|
||||
initStyleOption(&tab, i);
|
||||
if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
|
||||
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 (vertical)
|
||||
tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
|
||||
tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
|
||||
else
|
||||
tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
|
||||
tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
|
||||
}
|
||||
if (!d->dragInProgress)
|
||||
p.drawControl(QStyle::CE_TabBarTab, tab);
|
||||
p.drawControl(QStyle::CE_TabBarTab, tabOption);
|
||||
else {
|
||||
int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
|
||||
if (verticalTabs(d->shape))
|
||||
d->movingTab->setGeometry(tab.rect.adjusted(0, -taboverlap, 0, taboverlap));
|
||||
d->movingTab->setGeometry(tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap));
|
||||
else
|
||||
d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
|
||||
d->movingTab->setGeometry(tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1924,7 +1922,7 @@ void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool rem
|
|||
if (remove || (index == firstVisible)) {
|
||||
firstVisible = -1;
|
||||
for (int i = 0; i < tabList.count(); ++i) {
|
||||
if (tabList.at(i).visible) {
|
||||
if (tabList.at(i)->visible) {
|
||||
firstVisible = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1935,7 +1933,7 @@ void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool rem
|
|||
if (remove || (index == lastVisible)) {
|
||||
lastVisible = -1;
|
||||
for (int i = tabList.count() - 1; i >= 0; --i) {
|
||||
if (tabList.at(i).visible) {
|
||||
if (tabList.at(i)->visible) {
|
||||
lastVisible = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1999,53 +1997,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 : qAsConst(d->tabList))
|
||||
tab->lastTab = d->calculateNewPosition(from, to, tab->lastTab);
|
||||
|
||||
// update external variables
|
||||
int previousIndex = d->currentIndex;
|
||||
|
|
@ -2054,7 +2056,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;
|
||||
|
|
@ -2086,15 +2089,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();
|
||||
}
|
||||
|
|
@ -2166,7 +2169,7 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event)
|
|||
} else {
|
||||
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)
|
||||
|
|
@ -2243,10 +2246,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)
|
||||
|
|
@ -2260,17 +2264,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 : qAsConst(tabList)) {
|
||||
if (tab->animation && tab->animation->state() == QAbstractAnimation::Running) {
|
||||
allAnimationsFinished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // animation
|
||||
if (allAnimationsFinished && cleanup) {
|
||||
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 : qAsConst(tabList)) {
|
||||
tab->dragOffset = 0;
|
||||
}
|
||||
if (pressedIndex != -1 && movable) {
|
||||
pressedIndex = -1;
|
||||
|
|
@ -2281,7 +2286,7 @@ void QTabBarPrivate::moveTabFinished(int index)
|
|||
} else {
|
||||
if (!validIndex(index))
|
||||
return;
|
||||
tabList[index].dragOffset = 0;
|
||||
tabList.at(index)->dragOffset = 0;
|
||||
}
|
||||
q->update();
|
||||
}
|
||||
|
|
@ -2297,13 +2302,13 @@ void QTabBar::mouseReleaseEvent(QMouseEvent *event)
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
@ -2373,7 +2378,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) {
|
||||
q->setCurrentIndex(index);
|
||||
break;
|
||||
}
|
||||
|
|
@ -2502,14 +2507,14 @@ void QTabBar::setTabsClosable(bool closable)
|
|||
d->closeButtonOnTabs = closable;
|
||||
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 = nullptr;
|
||||
for (auto tab : qAsConst(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 = nullptr;
|
||||
if (closeSide == RightSide && tab->rightWidget) {
|
||||
tab->rightWidget->deleteLater();
|
||||
tab->rightWidget = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2731,14 +2736,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();
|
||||
|
|
@ -2752,12 +2758,11 @@ void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
|
|||
QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
|
||||
{
|
||||
Q_D(const QTabBar);
|
||||
if (index < 0 || index >= d->tabList.count())
|
||||
return nullptr;
|
||||
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
|
||||
|
|
@ -2849,12 +2854,12 @@ void CloseButton::paintEvent(QPaintEvent *)
|
|||
#if QT_CONFIG(animation)
|
||||
void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(const QVariant ¤t)
|
||||
{
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#define ANIMATION_DURATION 250
|
||||
|
||||
#include <qstyleoption.h>
|
||||
#include <utility>
|
||||
|
||||
QT_REQUIRE_CONFIG(tabbar);
|
||||
|
||||
|
|
@ -93,6 +94,10 @@ public:
|
|||
paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false),
|
||||
autoHide(false), changeCurrentOnDrag(false)
|
||||
{}
|
||||
~QTabBarPrivate()
|
||||
{
|
||||
qDeleteAll(tabList);
|
||||
}
|
||||
|
||||
QRect hoverRect;
|
||||
QPoint dragStartPosition;
|
||||
|
|
@ -131,8 +136,14 @@ public:
|
|||
struct Tab {
|
||||
inline Tab(const QIcon &ico, const QString &txt)
|
||||
: text(txt), icon(ico), enabled(true), visible(true)
|
||||
{}
|
||||
bool operator==(const Tab &other) const { return &other == this; }
|
||||
{
|
||||
}
|
||||
/*
|
||||
Tabs are managed by instance; they are not the same even
|
||||
if all properties are the same.
|
||||
*/
|
||||
Q_DISABLE_COPY_MOVE(Tab);
|
||||
|
||||
QString text;
|
||||
#if QT_CONFIG(tooltip)
|
||||
QString toolTip;
|
||||
|
|
@ -159,7 +170,6 @@ public:
|
|||
uint visible : 1;
|
||||
|
||||
#if QT_CONFIG(animation)
|
||||
~Tab() { delete animation; }
|
||||
struct TabBarAnimation : public QVariantAnimation {
|
||||
TabBarAnimation(Tab *t, QTabBarPrivate *_priv) : tab(t), priv(_priv)
|
||||
{ setEasingCurve(QEasingCurve::InOutQuad); }
|
||||
|
|
@ -171,15 +181,16 @@ public:
|
|||
//these are needed for the callbacks
|
||||
Tab *tab;
|
||||
QTabBarPrivate *priv;
|
||||
} *animation = nullptr;
|
||||
};
|
||||
std::unique_ptr<TabBarAnimation> animation;
|
||||
|
||||
void startAnimation(QTabBarPrivate *priv, int duration) {
|
||||
if (!priv->isAnimated()) {
|
||||
priv->moveTabFinished(priv->tabList.indexOf(*this));
|
||||
priv->moveTabFinished(priv->tabList.indexOf(this));
|
||||
return;
|
||||
}
|
||||
if (!animation)
|
||||
animation = new TabBarAnimation(this, priv);
|
||||
animation = std::make_unique<TabBarAnimation>(this, priv);
|
||||
animation->setStartValue(dragOffset);
|
||||
animation->setEndValue(0);
|
||||
animation->setDuration(duration);
|
||||
|
|
@ -190,7 +201,7 @@ public:
|
|||
{ Q_UNUSED(duration); priv->moveTabFinished(priv->tabList.indexOf(*this)); }
|
||||
#endif // animation
|
||||
};
|
||||
QList<Tab> tabList;
|
||||
QList<Tab*> tabList;
|
||||
mutable QHash<QString, QSize> textSizes;
|
||||
|
||||
void calculateFirstLastVisible(int index, bool visible, bool remove);
|
||||
|
|
@ -199,8 +210,8 @@ public:
|
|||
void slide(int from, int to);
|
||||
void init();
|
||||
|
||||
Tab *at(int index);
|
||||
const Tab *at(int index) const;
|
||||
inline Tab *at(int index) { return tabList.value(index, nullptr); }
|
||||
inline const Tab *at(int index) const { return tabList.value(index, nullptr); }
|
||||
|
||||
int indexAtPos(const QPoint &p) const;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue