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/qcore_mac_p.h>
#include <private/qcombobox_p.h> #include <private/qcombobox_p.h>
#include <private/qtabbar_p.h>
#include <private/qpainter_p.h> #include <private/qpainter_p.h>
#include <qapplication.h> #include <qapplication.h>
#include <qbitmap.h> #include <qbitmap.h>
@ -190,11 +191,33 @@ static const QColor mainWindowGradientEnd(200, 200, 200);
static const int DisclosureOffset = 4; 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. // Resolve these at run-time, since the functions was moved in Leopard.
typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *); typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0; static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
static int closeButtonSize = 12;
#ifndef QT_NO_TABBAR #ifndef QT_NO_TABBAR
static bool isVerticalTabs(const QTabBar::Shape shape) { static bool isVerticalTabs(const QTabBar::Shape shape) {
return (shape == QTabBar::RoundedEast 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); p->setRenderHints(QPainter::Antialiasing);
QRect rect(0, 0, closeButtonSize, closeButtonSize); QRect rect(0, 0, closeButtonSize, closeButtonSize);
QColor background; const int width = rect.width();
const int height = rect.height();
if (hover) { if (hover) {
background = QColor(124, 124, 124); // draw background circle
QColor background;
if (selected) {
background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered;
} else { } else {
if (active) { background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered;
if (selected)
background = QColor(104, 104, 104);
else
background = QColor(83, 83, 83);
} else {
if (selected)
background = QColor(144, 144, 144);
else
background = QColor(114, 114, 114);
}
} }
p->setPen(Qt::transparent); p->setPen(Qt::transparent);
p->setBrush(background); p->setBrush(background);
p->drawEllipse(rect); p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius);
}
// draw cross // draw cross
int min = 3; const int margin = 3;
int max = 9;
QPen crossPen; QPen crossPen;
crossPen.setColor(QColor(194, 194, 194)); crossPen.setColor(selected ? tabBarCloseButtonCrossSelected : tabBarCloseButtonCross);
crossPen.setWidthF(1.3); crossPen.setWidthF(1.1);
crossPen.setCapStyle(Qt::FlatCap); crossPen.setCapStyle(Qt::FlatCap);
p->setPen(crossPen); p->setPen(crossPen);
p->drawLine(min, min, max, max); p->drawLine(margin, margin, width - margin, height - margin);
p->drawLine(min, max, max, min); p->drawLine(margin, height - margin, width - margin, margin);
} }
#ifndef QT_NO_TABBAR #ifndef QT_NO_TABBAR
@ -278,108 +295,71 @@ QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
return 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; QRect rect = tabOpt->rect;
p->translate(tabOpt->rect.x(), tabOpt->rect.y());
r.moveLeft(0);
r.moveTop(0);
QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
int width = tabRect.width(); switch (tabOpt->shape) {
int height = 20; case QTabBar::RoundedNorth:
bool active = (tabOpt->state & QStyle::State_Active); case QTabBar::TriangularNorth:
bool selected = (tabOpt->state & QStyle::State_Selected); 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) { if (selected) {
QRect rect(1, 0, width - 2, height);
// fill body // fill body
if (tabOpt->documentMode && isUnified) { if (tabOpt->documentMode && isUnified) {
p->save(); p->save();
p->setCompositionMode(QPainter::CompositionMode_Source); p->setCompositionMode(QPainter::CompositionMode_Source);
p->fillRect(rect, QColor(Qt::transparent)); p->fillRect(tabRect, QColor(Qt::transparent));
p->restore(); p->restore();
} else if (active) { } else if (active) {
p->fillRect(rect, QColor(167, 167, 167)); p->fillRect(bodyRect, tabBarTabBackgroundActiveSelected);
// top line
p->fillRect(topLineRect, tabBarTabLineSelected);
} else { } else {
QLinearGradient gradient(rect.topLeft(), rect.bottomLeft()); p->fillRect(bodyRect, tabBarTabBackgroundSelected);
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);
} }
// 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 { } else {
// when the mouse is over non selected tabs they get a new color // 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) { if (hover) {
QRect rect(1, 2, width - 1, height - 1); // fill body
p->fillRect(rect, QColor(110, 110, 110)); p->fillRect(bodyRect, tabBarTabBackgroundActiveHovered);
} // bottom line
p->fillRect(bottomLineRect, tabBarTabLineActiveHovered);
// 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);
} }
} }
// 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) void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *w)
@ -390,53 +370,25 @@ void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *
} else { } else {
r.setHeight(w->height()); r.setHeight(w->height());
} }
QRect tabRect = rotateTabPainter(p, tbb->shape, r); const QRect tabRect = rotateTabPainter(p, tbb->shape, r);
int width = tabRect.width(); const int width = tabRect.width();
int height = tabRect.height(); const int height = tabRect.height();
bool active = (tbb->state & QStyle::State_Active); const bool active = (tbb->state & QStyle::State_Active);
// top border lines // fill body
QColor borderHighlightTop; const QRect bodyRect(0, 1, width, height - 1);
QColor borderTop; const QColor bodyColor = active ? tabBarTabBackgroundActive : tabBarTabBackground;
if (active) { p->fillRect(bodyRect, bodyColor);
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);
// center block // top line
QRect centralRect(tabRect.x(), 2, width, height - 2); const QRect topLineRect(0, 0, width, 1);
if (active) { const QColor topLineColor = active ? tabBarTabLineActive : tabBarTabLine;
QColor mainColor = QColor(120, 120, 120); p->fillRect(topLineRect, topLineColor);
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);
}
// bottom border lines // bottom line
QColor borderHighlightBottom; const QRect bottomLineRect(0, height - 1, width, 1);
QColor borderBottom; const QColor bottomLineColor = active ? tabBarTabLineActive : tabBarTabLine;
if (active) { p->fillRect(bottomLineRect, bottomLineColor);
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);
} }
#endif #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)); 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, QAquaWidgetSize QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option,
const QWidget *widg, const QWidget *widg,
QStyle::ContentsType ct, QStyle::ContentsType ct,
@ -2473,6 +2474,26 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
ret = int([NSWindow frameRectForContentRect:NSZeroRect ret = int([NSWindow frameRectForContentRect:NSZeroRect
styleMask:NSTitledWindowMask].size.height); styleMask:NSTitledWindowMask].size.height);
break; 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: case PM_TabBarTabVSpace:
ret = 4; ret = 4;
break; break;
@ -2481,10 +2502,10 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
ret = 0; ret = 0;
break; break;
case PM_TabBarBaseHeight: case PM_TabBarBaseHeight:
ret = 0; ret = 21;
break; break;
case PM_TabBarTabOverlap: case PM_TabBarTabOverlap:
ret = 0; ret = 1;
break; break;
case PM_TabBarBaseOverlap: case PM_TabBarBaseOverlap:
switch (d->aquaSizeConstrain(opt, widget)) { switch (d->aquaSizeConstrain(opt, widget)) {
@ -2661,20 +2682,6 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
case PM_LayoutHorizontalSpacing: case PM_LayoutHorizontalSpacing:
case PM_LayoutVerticalSpacing: case PM_LayoutVerticalSpacing:
return -1; 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: case PM_MenuHMargin:
ret = 0; ret = 0;
break; break;
@ -3526,10 +3533,18 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
case PE_FrameStatusBarItem: case PE_FrameStatusBarItem:
break; break;
case PE_IndicatorTabClose: { case PE_IndicatorTabClose: {
bool hover = (opt->state & State_MouseOver); // Make close button visible only on the hovered tab.
bool selected = (opt->state & State_Selected); if (QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget())) {
bool active = (opt->state & State_Active); const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar));
drawTabCloseButton(p, hover, active, selected); 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; } break;
case PE_PanelStatusBar: { case PE_PanelStatusBar: {
// Fill the status bar with the titlebar gradient. // 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 #ifndef QT_NO_TABBAR
case CE_TabBarTabShape: case CE_TabBarTabShape:
if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) { if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (tabOpt->documentMode) { if (tabOpt->documentMode) {
p->save(); p->save();
bool isUnified = false; bool isUnified = false;
@ -4076,7 +4090,9 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
isUnified = isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowTabStart.y()); 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(); p->restore();
return; return;
} }
@ -4202,23 +4218,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
} }
myTab.rect.setHeight(myTab.rect.height() + heightOffset); 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); QCommonStyle::drawControl(ce, &myTab, p, w);
} else { } else {
p->save(); p->save();
@ -4936,6 +4935,73 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
} }
} }
break; 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 #endif
case SE_LineEditContents: case SE_LineEditContents:
rect = QCommonStyle::subElementRect(sr, opt, widget); rect = QCommonStyle::subElementRect(sr, opt, widget);
@ -6543,13 +6609,14 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
if (vertTabs) if (vertTabs)
sz = sz.transposed(); sz = sz.transposed();
int defaultTabHeight; 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; QFontMetrics fm = opt->fontMetrics;
switch (AquaSize) { switch (AquaSize) {
case QAquaSizeUnknown: case QAquaSizeUnknown:
case QAquaSizeLarge: case QAquaSizeLarge:
if (tab->documentMode) if (tab->documentMode)
defaultTabHeight = 23; defaultTabHeight = 24;
else else
defaultTabHeight = 21; defaultTabHeight = 21;
break; break;
@ -6563,10 +6630,11 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
bool setWidth = false; bool setWidth = false;
if (differentFont || !tab->icon.isNull()) { if (differentFont || !tab->icon.isNull()) {
sz.rheight() = qMax(defaultTabHeight, sz.height()); sz.rheight() = qMax(defaultTabHeight, sz.height());
sz.rwidth() += extraHSpace;
} else { } else {
QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text); QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
sz.rheight() = qMax(defaultTabHeight, textSize.height()); sz.rheight() = qMax(defaultTabHeight, textSize.height());
sz.rwidth() = textSize.width() + defaultExtraSpace; sz.rwidth() = textSize.width() + extraVSpace;
setWidth = true; setWidth = true;
} }

View File

@ -229,6 +229,10 @@ public:
void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const; 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: public:
mutable QPointer<QObject> pressedButton; mutable QPointer<QObject> pressedButton;
mutable QPointer<QObject> defaultButton; mutable QPointer<QObject> defaultButton;

View File

@ -519,12 +519,14 @@ void QTabBarPrivate::layoutTabs()
maxExtent = maxWidth; maxExtent = maxWidth;
} }
Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure. if (!expanding) {
// Mirror our front item. // Mirror our front item.
tabChain[tabChainIndex].init(); tabChain[tabChainIndex].init();
tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight) tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
&& (tabAlignment != Qt::AlignJustify); && (tabAlignment != Qt::AlignJustify);
tabChain[tabChainIndex].empty = true; tabChain[tabChainIndex].empty = true;
}
Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
// Do the calculation // Do the calculation
qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0); 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) void QTabBarPrivate::makeVisible(int index)
{ {
Q_Q(QTabBar); Q_Q(QTabBar);
@ -1053,6 +1064,17 @@ void QTabBar::removeTab(int index)
} }
d->refresh(); d->refresh();
d->autoHideTabs(); 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); tabRemoved(index);
} }
} }
@ -1577,13 +1599,20 @@ bool QTabBar::event(QEvent *event)
QHoverEvent *he = static_cast<QHoverEvent *>(event); QHoverEvent *he = static_cast<QHoverEvent *>(event);
if (!d->hoverRect.contains(he->pos())) { if (!d->hoverRect.contains(he->pos())) {
QRect oldHoverRect = d->hoverRect; QRect oldHoverRect = d->hoverRect;
bool cursorOverTabs = false;
for (int i = 0; i < d->tabList.count(); ++i) { for (int i = 0; i < d->tabList.count(); ++i) {
QRect area = tabRect(i); QRect area = tabRect(i);
if (area.contains(he->pos())) { if (area.contains(he->pos())) {
d->hoverIndex = i;
d->hoverRect = area; d->hoverRect = area;
cursorOverTabs = true;
break; break;
} }
} }
if (!cursorOverTabs) {
d->hoverIndex = -1;
d->hoverRect = QRect();
}
if (he->oldPos() != QPoint(-1, -1)) if (he->oldPos() != QPoint(-1, -1))
update(oldHoverRect); update(oldHoverRect);
update(d->hoverRect); update(d->hoverRect);
@ -1591,6 +1620,7 @@ bool QTabBar::event(QEvent *event)
return true; return true;
} else if (event->type() == QEvent::HoverLeave) { } else if (event->type() == QEvent::HoverLeave) {
QRect oldHoverRect = d->hoverRect; QRect oldHoverRect = d->hoverRect;
d->hoverIndex = -1;
d->hoverRect = QRect(); d->hoverRect = QRect();
update(oldHoverRect); update(oldHoverRect);
return true; 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 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 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 \sa QTabWidget::documentMode
*/ */

View File

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

View File

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

View File

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