QMacStyle: update QTabBar style

Task-number: QTBUG-58266
Change-Id: I135e4dae44e2e97d73b7c7c97d8e682bcf459d75
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
This commit is contained in:
Oleg Yadrov 2017-01-31 13:38:01 -08:00
parent 5675334b6e
commit 175f33ed85
8 changed files with 466 additions and 205 deletions

View File

@ -52,6 +52,7 @@
#include <private/qcore_mac_p.h>
#include <private/qcombobox_p.h>
#include <private/qtabbar_p.h>
#include <private/qpainter_p.h>
#include <qapplication.h>
#include <qbitmap.h>
@ -190,11 +191,33 @@ static const QColor mainWindowGradientEnd(200, 200, 200);
static const int DisclosureOffset = 4;
// Tab bar colors
// active: window is active
// selected: tab is selected
// hovered: tab is hovered
static const QColor tabBarTabBackgroundActive(190, 190, 190);
static const QColor tabBarTabBackgroundActiveHovered(178, 178, 178);
static const QColor tabBarTabBackgroundActiveSelected(211, 211, 211);
static const QColor tabBarTabBackground(227, 227, 227);
static const QColor tabBarTabBackgroundSelected(246, 246, 246);
static const QColor tabBarTabLineActive(160, 160, 160);
static const QColor tabBarTabLineActiveHovered(150, 150, 150);
static const QColor tabBarTabLine(210, 210, 210);
static const QColor tabBarTabLineSelected(189, 189, 189);
static const QColor tabBarCloseButtonBackgroundHovered(162, 162, 162);
static const QColor tabBarCloseButtonBackgroundPressed(153, 153, 153);
static const QColor tabBarCloseButtonBackgroundSelectedHovered(192, 192, 192);
static const QColor tabBarCloseButtonBackgroundSelectedPressed(181, 181, 181);
static const QColor tabBarCloseButtonCross(100, 100, 100);
static const QColor tabBarCloseButtonCrossSelected(115, 115, 115);
static const int closeButtonSize = 14;
static const qreal closeButtonCornerRadius = 2.0;
// Resolve these at run-time, since the functions was moved in Leopard.
typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
static int closeButtonSize = 12;
#ifndef QT_NO_TABBAR
static bool isVerticalTabs(const QTabBar::Shape shape) {
return (shape == QTabBar::RoundedEast
@ -217,41 +240,35 @@ static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY)
}
void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed)
{
// draw background circle
p->setRenderHints(QPainter::Antialiasing);
QRect rect(0, 0, closeButtonSize, closeButtonSize);
QColor background;
const int width = rect.width();
const int height = rect.height();
if (hover) {
background = QColor(124, 124, 124);
} else {
if (active) {
if (selected)
background = QColor(104, 104, 104);
else
background = QColor(83, 83, 83);
// draw background circle
QColor background;
if (selected) {
background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered;
} else {
if (selected)
background = QColor(144, 144, 144);
else
background = QColor(114, 114, 114);
background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered;
}
p->setPen(Qt::transparent);
p->setBrush(background);
p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius);
}
p->setPen(Qt::transparent);
p->setBrush(background);
p->drawEllipse(rect);
// draw cross
int min = 3;
int max = 9;
const int margin = 3;
QPen crossPen;
crossPen.setColor(QColor(194, 194, 194));
crossPen.setWidthF(1.3);
crossPen.setColor(selected ? tabBarCloseButtonCrossSelected : tabBarCloseButtonCross);
crossPen.setWidthF(1.1);
crossPen.setCapStyle(Qt::FlatCap);
p->setPen(crossPen);
p->drawLine(min, min, max, max);
p->drawLine(min, max, max, min);
p->drawLine(margin, margin, width - margin, height - margin);
p->drawLine(margin, height - margin, width - margin, margin);
}
#ifndef QT_NO_TABBAR
@ -278,108 +295,71 @@ QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
return tabRect;
}
void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified)
void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified, int tabOverlap)
{
QRect r = tabOpt->rect;
p->translate(tabOpt->rect.x(), tabOpt->rect.y());
r.moveLeft(0);
r.moveTop(0);
QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
QRect rect = tabOpt->rect;
int width = tabRect.width();
int height = 20;
bool active = (tabOpt->state & QStyle::State_Active);
bool selected = (tabOpt->state & QStyle::State_Selected);
switch (tabOpt->shape) {
case QTabBar::RoundedNorth:
case QTabBar::TriangularNorth:
case QTabBar::RoundedSouth:
case QTabBar::TriangularSouth:
rect.adjust(-tabOverlap, 0, 0, 0);
break;
case QTabBar::RoundedEast:
case QTabBar::TriangularEast:
case QTabBar::RoundedWest:
case QTabBar::TriangularWest:
rect.adjust(0, -tabOverlap, 0, 0);
break;
default:
break;
}
p->translate(rect.x(), rect.y());
rect.moveLeft(0);
rect.moveTop(0);
const QRect tabRect = rotateTabPainter(p, tabOpt->shape, rect);
const int width = tabRect.width();
const int height = tabRect.height();
const bool active = (tabOpt->state & QStyle::State_Active);
const bool selected = (tabOpt->state & QStyle::State_Selected);
const QRect bodyRect(1, 1, width - 2, height - 2);
const QRect topLineRect(1, 0, width - 2, 1);
const QRect bottomLineRect(1, height - 1, width - 2, 1);
if (selected) {
QRect rect(1, 0, width - 2, height);
// fill body
if (tabOpt->documentMode && isUnified) {
p->save();
p->setCompositionMode(QPainter::CompositionMode_Source);
p->fillRect(rect, QColor(Qt::transparent));
p->fillRect(tabRect, QColor(Qt::transparent));
p->restore();
} else if (active) {
p->fillRect(rect, QColor(167, 167, 167));
p->fillRect(bodyRect, tabBarTabBackgroundActiveSelected);
// top line
p->fillRect(topLineRect, tabBarTabLineSelected);
} else {
QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
gradient.setColorAt(0, QColor(216, 216, 216));
gradient.setColorAt(0.5, QColor(215, 215, 215));
gradient.setColorAt(1, QColor(210, 210, 210));
p->fillRect(rect, gradient);
p->fillRect(bodyRect, tabBarTabBackgroundSelected);
}
// draw border
QColor borderSides;
QColor borderBottom;
if (active) {
borderSides = QColor(88, 88, 88);
borderBottom = QColor(88, 88, 88);
} else {
borderSides = QColor(121, 121, 121);
borderBottom = QColor(116, 116, 116);
}
p->setPen(borderSides);
int bottom = height;
// left line
p->drawLine(0, 1, 0, bottom-2);
// right line
p->drawLine(width-1, 1, width-1, bottom-2);
// bottom line
if (active) {
p->setPen(QColor(168, 168, 168));
p->drawLine(3, bottom-1, width-3, bottom-1);
}
p->setPen(borderBottom);
p->drawLine(2, bottom, width-2, bottom);
int w = 3;
QRectF rectangleLeft(1, height - w, w, w);
QRectF rectangleRight(width - 2, height - 1, w, w);
int startAngle = 180 * 16;
int spanAngle = 90 * 16;
p->setRenderHint(QPainter::Antialiasing);
p->drawArc(rectangleLeft, startAngle, spanAngle);
p->drawArc(rectangleRight, startAngle, -spanAngle);
} else {
// when the mouse is over non selected tabs they get a new color
bool hover = (tabOpt->state & QStyle::State_MouseOver);
const bool hover = (tabOpt->state & QStyle::State_MouseOver);
if (hover) {
QRect rect(1, 2, width - 1, height - 1);
p->fillRect(rect, QColor(110, 110, 110));
}
// seperator lines between tabs
bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
bool drawOnRight = !west;
if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
|| (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
QColor borderColor;
QColor borderHighlightColor;
if (active) {
borderColor = QColor(64, 64, 64);
borderHighlightColor = QColor(140, 140, 140);
} else {
borderColor = QColor(135, 135, 135);
borderHighlightColor = QColor(178, 178, 178);
}
int x = drawOnRight ? width : 0;
// tab seperator line
p->setPen(borderColor);
p->drawLine(x, 2, x, height + 1);
// tab seperator highlight
p->setPen(borderHighlightColor);
p->drawLine(x-1, 2, x-1, height + 1);
p->drawLine(x+1, 2, x+1, height + 1);
// fill body
p->fillRect(bodyRect, tabBarTabBackgroundActiveHovered);
// bottom line
p->fillRect(bottomLineRect, tabBarTabLineActiveHovered);
}
}
// separator lines between tabs
const QRect leftLineRect(0, 1, 1, height - 2);
const QRect rightLineRect(width - 1, 1, 1, height - 2);
const QColor separatorLineColor = active ? tabBarTabLineActive : tabBarTabLine;
p->fillRect(leftLineRect, separatorLineColor);
p->fillRect(rightLineRect, separatorLineColor);
}
void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *w)
@ -390,53 +370,25 @@ void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *
} else {
r.setHeight(w->height());
}
QRect tabRect = rotateTabPainter(p, tbb->shape, r);
int width = tabRect.width();
int height = tabRect.height();
bool active = (tbb->state & QStyle::State_Active);
const QRect tabRect = rotateTabPainter(p, tbb->shape, r);
const int width = tabRect.width();
const int height = tabRect.height();
const bool active = (tbb->state & QStyle::State_Active);
// top border lines
QColor borderHighlightTop;
QColor borderTop;
if (active) {
borderTop = QColor(64, 64, 64);
borderHighlightTop = QColor(174, 174, 174);
} else {
borderTop = QColor(135, 135, 135);
borderHighlightTop = QColor(207, 207, 207);
}
p->setPen(borderHighlightTop);
p->drawLine(tabRect.x(), 0, width, 0);
p->setPen(borderTop);
p->drawLine(tabRect.x(), 1, width, 1);
// fill body
const QRect bodyRect(0, 1, width, height - 1);
const QColor bodyColor = active ? tabBarTabBackgroundActive : tabBarTabBackground;
p->fillRect(bodyRect, bodyColor);
// center block
QRect centralRect(tabRect.x(), 2, width, height - 2);
if (active) {
QColor mainColor = QColor(120, 120, 120);
p->fillRect(centralRect, mainColor);
} else {
QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
gradient.setColorAt(0, QColor(165, 165, 165));
gradient.setColorAt(0.5, QColor(164, 164, 164));
gradient.setColorAt(1, QColor(158, 158, 158));
p->fillRect(centralRect, gradient);
}
// top line
const QRect topLineRect(0, 0, width, 1);
const QColor topLineColor = active ? tabBarTabLineActive : tabBarTabLine;
p->fillRect(topLineRect, topLineColor);
// bottom border lines
QColor borderHighlightBottom;
QColor borderBottom;
if (active) {
borderHighlightBottom = QColor(153, 153, 153);
borderBottom = QColor(64, 64, 64);
} else {
borderHighlightBottom = QColor(177, 177, 177);
borderBottom = QColor(127, 127, 127);
}
p->setPen(borderHighlightBottom);
p->drawLine(tabRect.x(), height - 2, width, height - 2);
p->setPen(borderBottom);
p->drawLine(tabRect.x(), height - 1, width, height - 1);
// bottom line
const QRect bottomLineRect(0, height - 1, width, 1);
const QColor bottomLineColor = active ? tabBarTabLineActive : tabBarTabLine;
p->fillRect(bottomLineRect, bottomLineColor);
}
#endif
@ -1105,6 +1057,55 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int h
QRect(focusRingPixmap.width() - shCornerSize, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
}
#ifndef QT_NO_TABBAR
void QMacStylePrivate::tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect) const
{
Q_ASSERT(textRect);
QRect tr = opt->rect;
const bool verticalTabs = opt->shape == QTabBar::RoundedEast
|| opt->shape == QTabBar::RoundedWest
|| opt->shape == QTabBar::TriangularEast
|| opt->shape == QTabBar::TriangularWest;
if (verticalTabs)
tr.setRect(0, 0, tr.height(), tr.width()); // 0, 0 as we will have a translate transform
int verticalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftVertical, opt, widget);
int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt, widget);
const int hpadding = 4;
const int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt, widget) / 2;
if (opt->shape == QTabBar::RoundedSouth || opt->shape == QTabBar::TriangularSouth)
verticalShift = -verticalShift;
tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding);
const bool selected = opt->state & QStyle::State_Selected;
if (selected) {
tr.setTop(tr.top() - verticalShift);
tr.setRight(tr.right() - horizontalShift);
}
// left widget
if (!opt->leftButtonSize.isEmpty()) {
const int buttonSize = verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width();
tr.setLeft(tr.left() + 4 + buttonSize);
// make text aligned to center
if (opt->rightButtonSize.isEmpty())
tr.setRight(tr.right() - 4 - buttonSize);
}
// right widget
if (!opt->rightButtonSize.isEmpty()) {
const int buttonSize = verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width();
tr.setRight(tr.right() - 4 - buttonSize);
// make text aligned to center
if (opt->leftButtonSize.isEmpty())
tr.setLeft(tr.left() + 4 + buttonSize);
}
if (!verticalTabs)
tr = proxyStyle->visualRect(opt->direction, opt->rect, tr);
*textRect = tr;
}
#endif //QT_NO_TABBAR
QAquaWidgetSize QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option,
const QWidget *widg,
QStyle::ContentsType ct,
@ -2473,6 +2474,26 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
ret = int([NSWindow frameRectForContentRect:NSZeroRect
styleMask:NSTitledWindowMask].size.height);
break;
case QStyle::PM_TabBarTabHSpace:
switch (d->aquaSizeConstrain(opt, widget)) {
case QAquaSizeLarge:
ret = QCommonStyle::pixelMetric(metric, opt, widget);
break;
case QAquaSizeSmall:
ret = 20;
break;
case QAquaSizeMini:
ret = 16;
break;
case QAquaSizeUnknown:
const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt);
if (tb && tb->documentMode)
ret = 24;
else
ret = QCommonStyle::pixelMetric(metric, opt, widget);
break;
}
break;
case PM_TabBarTabVSpace:
ret = 4;
break;
@ -2481,10 +2502,10 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
ret = 0;
break;
case PM_TabBarBaseHeight:
ret = 0;
ret = 21;
break;
case PM_TabBarTabOverlap:
ret = 0;
ret = 1;
break;
case PM_TabBarBaseOverlap:
switch (d->aquaSizeConstrain(opt, widget)) {
@ -2661,20 +2682,6 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
case PM_LayoutHorizontalSpacing:
case PM_LayoutVerticalSpacing:
return -1;
case QStyle::PM_TabBarTabHSpace:
switch (d->aquaSizeConstrain(opt, widget)) {
case QAquaSizeLarge:
case QAquaSizeUnknown:
ret = QCommonStyle::pixelMetric(metric, opt, widget);
break;
case QAquaSizeSmall:
ret = 20;
break;
case QAquaSizeMini:
ret = 16;
break;
}
break;
case PM_MenuHMargin:
ret = 0;
break;
@ -3526,10 +3533,18 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
case PE_FrameStatusBarItem:
break;
case PE_IndicatorTabClose: {
bool hover = (opt->state & State_MouseOver);
bool selected = (opt->state & State_Selected);
bool active = (opt->state & State_Active);
drawTabCloseButton(p, hover, active, selected);
// Make close button visible only on the hovered tab.
if (QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget())) {
const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar));
const int hoveredTabIndex = tabBarPrivate->hoveredTabIndex();
if (hoveredTabIndex != -1 && ((w == tabBar->tabButton(hoveredTabIndex, QTabBar::LeftSide)) ||
(w == tabBar->tabButton(hoveredTabIndex, QTabBar::RightSide)))) {
const bool hover = (opt->state & State_MouseOver);
const bool selected = (opt->state & State_Selected);
const bool pressed = (opt->state & State_Sunken);
drawTabCloseButton(p, hover, selected, pressed);
}
}
} break;
case PE_PanelStatusBar: {
// Fill the status bar with the titlebar gradient.
@ -4066,7 +4081,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
#ifndef QT_NO_TABBAR
case CE_TabBarTabShape:
if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (tabOpt->documentMode) {
p->save();
bool isUnified = false;
@ -4076,7 +4090,9 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
isUnified = isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowTabStart.y());
}
drawTabShape(p, tabOpt, isUnified);
const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, opt, w);
drawTabShape(p, tabOpt, isUnified, tabOverlap);
p->restore();
return;
}
@ -4202,23 +4218,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
myTab.rect.setHeight(myTab.rect.height() + heightOffset);
if (myTab.documentMode) {
p->save();
rotateTabPainter(p, myTab.shape, myTab.rect);
QColor shadowColor = QColor(myTab.documentMode ? Qt::white : Qt::black);
shadowColor.setAlpha(75);
QPalette np = tab->palette;
np.setColor(QPalette::WindowText, shadowColor);
QRect nr = proxy()->subElementRect(SE_TabBarTabText, opt, w);
nr.moveTop(-1);
int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextHideMnemonic;
proxy()->drawItemText(p, nr, alignment, np, tab->state & State_Enabled,
tab->text, QPalette::WindowText);
p->restore();
}
QCommonStyle::drawControl(ce, &myTab, p, w);
} else {
p->save();
@ -4936,6 +4935,73 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
}
}
break;
case SE_TabBarTabText:
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
d->tabLayout(tab, widget, &rect);
}
break;
case SE_TabBarTabLeftButton:
case SE_TabBarTabRightButton:
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
bool selected = tab->state & State_Selected;
int verticalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget);
int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget);
int hpadding = 5;
bool verticalTabs = tab->shape == QTabBar::RoundedEast
|| tab->shape == QTabBar::RoundedWest
|| tab->shape == QTabBar::TriangularEast
|| tab->shape == QTabBar::TriangularWest;
QRect tr = tab->rect;
if (tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::TriangularSouth)
verticalShift = -verticalShift;
if (verticalTabs) {
qSwap(horizontalShift, verticalShift);
horizontalShift *= -1;
verticalShift *= -1;
}
if (tab->shape == QTabBar::RoundedWest || tab->shape == QTabBar::TriangularWest)
horizontalShift = -horizontalShift;
tr.adjust(0, 0, horizontalShift, verticalShift);
if (selected)
{
tr.setBottom(tr.bottom() - verticalShift);
tr.setRight(tr.right() - horizontalShift);
}
QSize size = (sr == SE_TabBarTabLeftButton) ? tab->leftButtonSize : tab->rightButtonSize;
int w = size.width();
int h = size.height();
int midHeight = static_cast<int>(qCeil(float(tr.height() - h) / 2));
int midWidth = ((tr.width() - w) / 2);
bool atTheTop = true;
switch (tab->shape) {
case QTabBar::RoundedWest:
case QTabBar::TriangularWest:
atTheTop = (sr == SE_TabBarTabLeftButton);
break;
case QTabBar::RoundedEast:
case QTabBar::TriangularEast:
atTheTop = (sr == SE_TabBarTabRightButton);
break;
default:
if (sr == SE_TabBarTabLeftButton)
rect = QRect(tab->rect.x() + hpadding, midHeight, w, h);
else
rect = QRect(tab->rect.right() - w - hpadding, midHeight, w, h);
rect = visualRect(tab->direction, tab->rect, rect);
}
if (verticalTabs) {
if (atTheTop)
rect = QRect(midWidth, tr.y() + tab->rect.height() - hpadding - h, w, h);
else
rect = QRect(midWidth, tr.y() + hpadding, w, h);
}
}
break;
#endif
case SE_LineEditContents:
rect = QCommonStyle::subElementRect(sr, opt, widget);
@ -6543,13 +6609,14 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
if (vertTabs)
sz = sz.transposed();
int defaultTabHeight;
int defaultExtraSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget); // Remove spurious gcc warning (AFAIK)
int extraHSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget);
int extraVSpace = proxy()->pixelMetric(PM_TabBarTabVSpace, tab, widget);
QFontMetrics fm = opt->fontMetrics;
switch (AquaSize) {
case QAquaSizeUnknown:
case QAquaSizeLarge:
if (tab->documentMode)
defaultTabHeight = 23;
defaultTabHeight = 24;
else
defaultTabHeight = 21;
break;
@ -6563,10 +6630,11 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
bool setWidth = false;
if (differentFont || !tab->icon.isNull()) {
sz.rheight() = qMax(defaultTabHeight, sz.height());
sz.rwidth() += extraHSpace;
} else {
QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
sz.rheight() = qMax(defaultTabHeight, textSize.height());
sz.rwidth() = textSize.width() + defaultExtraSpace;
sz.rwidth() = textSize.width() + extraVSpace;
setWidth = true;
}

View File

@ -229,6 +229,10 @@ public:
void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const;
#ifndef QT_NO_TABBAR
void tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect) const;
#endif
public:
mutable QPointer<QObject> pressedButton;
mutable QPointer<QObject> defaultButton;

View File

@ -519,12 +519,14 @@ void QTabBarPrivate::layoutTabs()
maxExtent = maxWidth;
}
if (!expanding) {
// Mirror our front item.
tabChain[tabChainIndex].init();
tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
&& (tabAlignment != Qt::AlignJustify);
tabChain[tabChainIndex].empty = true;
}
Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
// Mirror our front item.
tabChain[tabChainIndex].init();
tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
&& (tabAlignment != Qt::AlignJustify);
tabChain[tabChainIndex].empty = true;
// Do the calculation
qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
@ -664,6 +666,15 @@ QRect QTabBarPrivate::normalizedScrollRect(int index)
}
}
int QTabBarPrivate::hoveredTabIndex() const
{
if (dragInProgress)
return currentIndex;
if (hoverIndex >= 0)
return hoverIndex;
return -1;
}
void QTabBarPrivate::makeVisible(int index)
{
Q_Q(QTabBar);
@ -1053,6 +1064,17 @@ 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;
}
}
update(d->hoverRect);
}
tabRemoved(index);
}
}
@ -1577,20 +1599,28 @@ bool QTabBar::event(QEvent *event)
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->hoverRect = QRect();
}
if (he->oldPos() != QPoint(-1, -1))
update(oldHoverRect);
update(d->hoverRect);
}
return true;
} else if (event->type() == QEvent::HoverLeave ) {
} else if (event->type() == QEvent::HoverLeave) {
QRect oldHoverRect = d->hoverRect;
d->hoverIndex = -1;
d->hoverRect = QRect();
update(oldHoverRect);
return true;
@ -2435,7 +2465,7 @@ void QTabBar::setMovable(bool movable)
This property is used as a hint for styles to draw the tabs in a different
way then they would normally look in a tab widget. On \macos this will
look similar to the tabs in Safari or Leopard's Terminal.app.
look similar to the tabs in Safari or Sierra's Terminal.app.
\sa QTabWidget::documentMode
*/

View File

@ -87,7 +87,7 @@ class QTabBarPrivate : public QWidgetPrivate
public:
QTabBarPrivate()
:currentIndex(-1), pressedIndex(-1), shape(QTabBar::RoundedNorth), layoutDirty(false),
drawBase(true), scrollOffset(0), elideModeSetByUser(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false),
drawBase(true), scrollOffset(0), hoverIndex(-1), elideModeSetByUser(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false),
selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false),
dragInProgress(false), documentMode(false), autoHide(false), changeCurrentOnDrag(false),
switchTabCurrentIndex(-1), switchTabTimerId(0), movingTab(0)
@ -192,6 +192,7 @@ public:
void moveTab(int index, int offset);
void moveTabFinished(int index);
QRect hoverRect;
int hoverIndex;
void refresh();
void layoutTabs();
@ -202,6 +203,7 @@ public:
void setupMovableTab();
void autoHideTabs();
QRect normalizedScrollRect(int index = -1);
int hoveredTabIndex() const;
void initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const;

View File

@ -209,7 +209,6 @@ public:
bool dirty;
QTabWidget::TabPosition pos;
QTabWidget::TabShape shape;
int alignment;
QWidget *leftCornerWidget;
QWidget *rightCornerWidget;
};

View File

@ -52,7 +52,8 @@ xembed-widgets \
shortcuts \
dialogs \
windowtransparency \
unc
unc \
qtabbar
!qtConfig(openssl): SUBDIRS -= qssloptions

View File

@ -0,0 +1,153 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QApplication>
#include <QWidget>
#include <QStackedWidget>
#include <QTabBar>
#include <QLabel>
#include <QLayout>
#include <QDesktopWidget>
#include <QTabWidget>
const int TabCount = 5;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget;
QStackedWidget stackedWidget;
QTabBar tabBar;
tabBar.setDocumentMode(true);
tabBar.setTabsClosable(true);
tabBar.setMovable(true);
tabBar.setExpanding(true);
// top
tabBar.setShape(QTabBar::RoundedNorth);
// bottom
// tabBar.setShape(QTabBar::RoundedSouth);
// left
// tabBar.setShape(QTabBar::RoundedWest);
// right
// tabBar.setShape(QTabBar::RoundedEast);
QMap<int, QWidget*> tabs;
for (int i = 0; i < TabCount; i++) {
QString tabNumberString = QString::number(i);
QLabel *label = new QLabel(QStringLiteral("Tab %1 content").arg(tabNumberString));
tabs[i] = label;
label->setAlignment(Qt::AlignCenter);
stackedWidget.addWidget(label);
tabBar.addTab(QStringLiteral("Tab %1").arg(tabNumberString));
}
QObject::connect(&tabBar, &QTabBar::tabMoved, [&tabs](int from, int to) {
QWidget *thisWidget = tabs[from];
QWidget *thatWidget = tabs[to];
tabs[from] = thatWidget;
tabs[to] = thisWidget;
});
QObject::connect(&tabBar, &QTabBar::currentChanged, [&stackedWidget, &tabs](int index) {
if (index >= 0)
stackedWidget.setCurrentWidget(tabs[index]);
});
QObject::connect(&tabBar, &QTabBar::tabCloseRequested, [&stackedWidget, &tabBar, &tabs](int index) {
QWidget *widget = tabs[index];
tabBar.removeTab(index);
for (int i = index + 1; i < TabCount; i++)
tabs[i-1] = tabs[i];
int currentIndex = tabBar.currentIndex();
if (currentIndex >= 0)
stackedWidget.setCurrentWidget(tabs[currentIndex]);
delete widget;
});
QLayout *layout;
switch (tabBar.shape()) {
case QTabBar::RoundedEast:
case QTabBar::TriangularEast:
tabBar.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
layout = new QHBoxLayout(&widget);
layout->addWidget(&stackedWidget);
layout->addWidget(&tabBar);
break;
case QTabBar::RoundedWest:
case QTabBar::TriangularWest:
tabBar.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
layout = new QHBoxLayout(&widget);
layout->addWidget(&tabBar);
layout->addWidget(&stackedWidget);
break;
case QTabBar::RoundedNorth:
case QTabBar::TriangularNorth:
tabBar.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
layout = new QVBoxLayout(&widget);
layout->addWidget(&tabBar);
layout->addWidget(&stackedWidget);
break;
case QTabBar::RoundedSouth:
case QTabBar::TriangularSouth:
tabBar.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
layout = new QVBoxLayout(&widget);
layout->addWidget(&stackedWidget);
layout->addWidget(&tabBar);
break;
}
layout->setMargin(0);
widget.resize(QApplication::desktop()->screenGeometry(&widget).size() * 0.5);
widget.show();
return app.exec();
}

View File

@ -0,0 +1,4 @@
TARGET = qtabbar
TEMPLATE = app
QT = core gui widgets
SOURCES = main.cpp