QTabBar: Cache title text sizes

The first part adds QTabBarPrivate::initBasicStyleOption()
which is basically QTabBar::initStyleOption() but
without the expensive QFontMetrics::elidedText() call.
That is because QTabBar::tabSizeHint() would call
initStyleOption() and then immediately discard the result
of that computation.

Then, QTabBar::tabSizeHint() is modified to cache the calls
to QFontMetrics::size(), which is also expensive. The cache
is invalidated when the style or the font changes, or when
the elide mode is set.

Change-Id: I591b2e401af3576a2ebabc5b94f19ae157e28cf2
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Wayne Arnold <wayne.arnold@autodesk.com>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Gabriel de Dietrich 2016-05-20 10:47:37 -07:00
parent 8ebe8ae35e
commit 89d7c904e5
2 changed files with 58 additions and 32 deletions

View File

@ -134,60 +134,59 @@ void QTabBarPrivate::updateMacBorderMetrics()
}
/*!
Initialize \a option with the values from the tab at \a tabIndex. This method
is useful for subclasses when they need a QStyleOptionTab,
but don't want to fill in all the information themselves.
\sa QStyleOption::initFrom(), QTabWidget::initStyleOption()
\internal
This is basically QTabBar::initStyleOption() but
without the expensive QFontMetrics::elidedText() call.
*/
void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const
{
Q_D(const QTabBar);
int totalTabs = d->tabList.size();
Q_Q(const QTabBar);
const int totalTabs = tabList.size();
if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
return;
const QTabBarPrivate::Tab &tab = d->tabList.at(tabIndex);
option->initFrom(this);
const QTabBarPrivate::Tab &tab = tabList.at(tabIndex);
option->initFrom(q);
option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
option->rect = tabRect(tabIndex);
bool isCurrent = tabIndex == d->currentIndex;
option->rect = q->tabRect(tabIndex);
const bool isCurrent = tabIndex == currentIndex;
option->row = 0;
if (tabIndex == d->pressedIndex)
if (tabIndex == pressedIndex)
option->state |= QStyle::State_Sunken;
if (isCurrent)
option->state |= QStyle::State_Selected;
if (isCurrent && hasFocus())
if (isCurrent && q->hasFocus())
option->state |= QStyle::State_HasFocus;
if (!tab.enabled)
option->state &= ~QStyle::State_Enabled;
if (isActiveWindow())
if (q->isActiveWindow())
option->state |= QStyle::State_Active;
if (!d->dragInProgress && option->rect == d->hoverRect)
if (!dragInProgress && option->rect == hoverRect)
option->state |= QStyle::State_MouseOver;
option->shape = d->shape;
option->shape = shape;
option->text = tab.text;
if (tab.textColor.isValid())
option->palette.setColor(foregroundRole(), tab.textColor);
option->palette.setColor(q->foregroundRole(), tab.textColor);
option->icon = tab.icon;
option->iconSize = iconSize(); // Will get the default value then.
option->iconSize = q->iconSize(); // Will get the default value then.
option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
option->documentMode = d->documentMode;
option->documentMode = documentMode;
if (tabIndex > 0 && tabIndex - 1 == d->currentIndex)
if (tabIndex > 0 && tabIndex - 1 == currentIndex)
option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
else if (tabIndex + 1 < totalTabs && tabIndex + 1 == d->currentIndex)
else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
option->selectedPosition = QStyleOptionTab::NextIsSelected;
else
option->selectedPosition = QStyleOptionTab::NotAdjacent;
bool paintBeginning = (tabIndex == 0) || (d->dragInProgress && tabIndex == d->pressedIndex + 1);
bool paintEnd = (tabIndex == totalTabs - 1) || (d->dragInProgress && tabIndex == d->pressedIndex - 1);
const bool paintBeginning = (tabIndex == 0) || (dragInProgress && tabIndex == pressedIndex + 1);
const bool paintEnd = (tabIndex == totalTabs - 1) || (dragInProgress && tabIndex == pressedIndex - 1);
if (paintBeginning) {
if (paintEnd)
option->position = QStyleOptionTab::OnlyOneTab;
@ -200,7 +199,7 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
}
#ifndef QT_NO_TABWIDGET
if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(parentWidget())) {
if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(q->parentWidget())) {
option->features |= QStyleOptionTab::HasFrame;
if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
@ -208,6 +207,19 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
}
#endif
}
/*!
Initialize \a option with the values from the tab at \a tabIndex. This method
is useful for subclasses when they need a QStyleOptionTab,
but don't want to fill in all the information themselves.
\sa QStyleOption::initFrom(), QTabWidget::initStyleOption()
*/
void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
{
Q_D(const QTabBar);
d->initBasicStyleOption(option, tabIndex);
QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
@ -1020,6 +1032,7 @@ void QTabBar::setTabText(int index, const QString &text)
{
Q_D(QTabBar);
if (QTabBarPrivate::Tab *tab = d->at(index)) {
d->textSizes.remove(tab->text);
tab->text = text;
#ifndef QT_NO_SHORTCUT
releaseShortcut(tab->shortcutId);
@ -1379,7 +1392,7 @@ QSize QTabBar::tabSizeHint(int index) const
Q_D(const QTabBar);
if (const QTabBarPrivate::Tab *tab = d->at(index)) {
QStyleOptionTab opt;
initStyleOption(&opt, index);
d->initBasicStyleOption(&opt, index);
opt.text = d->tabList.at(index).text;
QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
@ -1405,13 +1418,16 @@ QSize QTabBar::tabSizeHint(int index) const
if (!opt.icon.isNull())
padding += 4;
QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
if (it == d->textSizes.end())
it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
const int textWidth = it.value().width();
QSize csz;
if (verticalTabs(d->shape)) {
csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe + widgetHeight + padding);
textWidth + iconSize.width() + hframe + widgetHeight + padding);
} else {
csz = QSize(fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe
+ widgetWidth + padding,
csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
}
@ -2071,15 +2087,21 @@ void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
void QTabBar::changeEvent(QEvent *event)
{
Q_D(QTabBar);
if (event->type() == QEvent::StyleChange) {
switch (event->type()) {
case QEvent::StyleChange:
if (!d->elideModeSetByUser)
d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
if (!d->useScrollButtonsSetByUser)
d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
// fallthrough
case QEvent::FontChange:
d->textSizes.clear();
d->refresh();
} else if (event->type() == QEvent::FontChange) {
d->refresh();
break;
default:
break;
}
QWidget::changeEvent(event);
}
@ -2122,6 +2144,7 @@ void QTabBar::setElideMode(Qt::TextElideMode mode)
Q_D(QTabBar);
d->elideMode = mode;
d->elideModeSetByUser = true;
d->textSizes.clear();
d->refresh();
}

View File

@ -159,6 +159,7 @@ public:
#endif //QT_NO_ANIMATION
};
QList<Tab> tabList;
mutable QHash<QString, QSize> textSizes;
int calculateNewPosition(int from, int to, int index) const;
void slide(int from, int to);
@ -192,6 +193,8 @@ public:
void setupMovableTab();
void autoHideTabs();
void initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const;
void makeVisible(int index);
QSize iconSize;
Qt::TextElideMode elideMode;