QMacStyle: CE_TabBarTabShape is now HITheme-free

This should be the last bit of QMacStyle depending
on HIToolBox APIs.

Small and mini sizes support coming later. Same
for the focus ring.

Change-Id: If625fccb87ebd0607b96a8c5040888df5a2a1032
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
bb10
Gabriel de Dietrich 2018-04-02 18:40:39 -07:00
parent c3d2862ed3
commit 375dd7ad34
2 changed files with 165 additions and 105 deletions

View File

@ -1177,6 +1177,7 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int
switch (cw.type) {
case Box:
case Button_SquareButton:
case SegmentedControl_Middle:
case TextField: {
auto innerRect = targetRect;
if (cw.type == TextField)
@ -1215,7 +1216,8 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int
}
case Button_PopupButton:
case Button_PullDown:
case Button_PushButton: {
case Button_PushButton:
case SegmentedControl_Single: {
const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4;
const qreal outterRadius = innerRadius + focusRingWidth;
hOffset = targetRect.left();
@ -1226,7 +1228,9 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int
focusRingPath.addRoundedRect(outterRect, outterRadius, outterRadius);
break;
}
case ComboBox: {
case ComboBox:
case SegmentedControl_First:
case SegmentedControl_Last: {
hOffset = targetRect.left();
vOffset = targetRect.top();
const qreal innerRadius = 8;
@ -1271,6 +1275,9 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int
p->save();
p->setRenderHint(QPainter::Antialiasing);
p->setOpacity(0.5);
if (cw.type == SegmentedControl_First) {
// TODO Flip left-right
}
p->translate(hOffset, vOffset);
p->fillPath(focusRingPath, focusRingColor);
p->restore();
@ -1749,6 +1756,7 @@ QRectF QMacStylePrivate::comboboxEditBounds(const QRectF &outterBounds, const Co
ret.adjust(13, 4, -20, -3);
break;
case QStyleHelper::SizeMini:
// FIXME Wrong
ret.adjust(16, 5, -19, 0);
ret.setHeight(13);
break;
@ -3547,6 +3555,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
d->drawNSViewInRect(cw, pb, frameRect, p, true, ^(CGContextRef __unused ctx, const CGRect &r) {
[pb.cell drawBezelWithFrame:r inView:pb.superview];
});
[pb highlight:NO];
if (hasMenu && cw.type == QMacStylePrivate::Button_SquareButton) {
// Using -[NSPopuButtonCell drawWithFrame:inView:] above won't do
@ -3669,7 +3678,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
#endif // #if QT_CONFIG(combobox)
#if QT_CONFIG(tabbar)
case CE_TabBarTabShape:
if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (const auto *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (tabOpt->documentMode) {
p->save();
bool isUnified = false;
@ -3686,111 +3695,157 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
return;
}
HIThemeTabDrawInfo tdi;
tdi.version = 1;
tdi.style = kThemeTabNonFront;
tdi.direction = (ThemeTabDirection)QMacStylePrivate::tabDirection(tabOpt->shape);
switch (d->aquaSizeConstrain(opt, w)) {
case QStyleHelper::SizeDefault:
case QStyleHelper::SizeLarge:
tdi.size = kHIThemeTabSizeNormal;
break;
case QStyleHelper::SizeSmall:
tdi.size = kHIThemeTabSizeSmall;
break;
case QStyleHelper::SizeMini:
tdi.size = kHIThemeTabSizeMini;
break;
}
bool verticalTabs = tdi.direction == kThemeTabWest || tdi.direction == kThemeTabEast;
QRect tabRect = tabOpt->rect;
bool selected = tabOpt->state & State_Selected;
if (selected) {
if (!(tabOpt->state & State_Active))
tdi.style = kThemeTabFrontUnavailable;
else if (!(tabOpt->state & State_Enabled))
tdi.style = kThemeTabFrontInactive;
else
tdi.style = kThemeTabFront;
} else if (!(tabOpt->state & State_Active)) {
tdi.style = kThemeTabNonFrontUnavailable;
} else if (!(tabOpt->state & State_Enabled)) {
tdi.style = kThemeTabNonFrontInactive;
} else if (tabOpt->state & State_Sunken) {
tdi.style = kThemeTabNonFrontPressed;
}
if (tabOpt->state & State_HasFocus)
tdi.adornment = kHIThemeTabAdornmentFocus;
else
tdi.adornment = kHIThemeTabAdornmentNone;
tdi.kind = kHIThemeTabKindNormal;
const bool isEnabled = tabOpt->state & State_Enabled;
const bool isPressed = tabOpt->state & State_Sunken;
const bool isSelected = tabOpt->state & State_Selected;
const auto tabDirection = QMacStylePrivate::tabDirection(tabOpt->shape);
const bool verticalTabs = tabDirection == QMacStylePrivate::East
|| tabDirection == QMacStylePrivate::West;
QStyleOptionTab::TabPosition tp = tabOpt->position;
QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition;
if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) {
if (tp == QStyleOptionTab::Beginning)
tp = QStyleOptionTab::End;
else if (tp == QStyleOptionTab::End)
tp = QStyleOptionTab::Beginning;
if (sp == QStyleOptionTab::NextIsSelected)
sp = QStyleOptionTab::PreviousIsSelected;
else if (sp == QStyleOptionTab::PreviousIsSelected)
sp = QStyleOptionTab::NextIsSelected;
switch (tp) {
case QStyleOptionTab::Beginning:
tp = QStyleOptionTab::End;
break;
case QStyleOptionTab::End:
tp = QStyleOptionTab::Beginning;
break;
default:
break;
}
}
bool stretchTabs = (!verticalTabs && tabRect.height() > 22) || (verticalTabs && tabRect.width() > 22);
// Alas, NSSegmentedControl and NSSegmentedCell are letting us down.
// We're not able to draw it at will, either calling -[drawSegment:
// inFrame:withView:], -[drawRect:] or anything in between. Besides,
// there's no public API do draw the pressed state, AFAICS. We'll use
// a push NSButton instead and clip the CGContext.
const auto cs = d->effectiveAquaSizeConstrain(opt, w);
// Extra hack to get the proper pressed appreance when not selected
const auto ct = isSelected || tp == QStyleOptionTab::OnlyOneTab ?
QMacStylePrivate::Button_PushButton :
QMacStylePrivate::Button_PopupButton;
const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
auto *pb = static_cast<NSButton *>(d->cocoaControl(cw));
auto vOffset = isSelected ? 2 : 1;
if (tabDirection == QMacStylePrivate::East)
vOffset -= 1;
const auto outterAdjust = isSelected ? 4 : 1;
const auto innerAdjust = isSelected ? 10 : 20;
QRectF frameRect = tabOpt->rect;
if (verticalTabs)
frameRect = QRectF(frameRect.y(), frameRect.x(), frameRect.height(), frameRect.width());
// Adjust before clipping
frameRect = frameRect.translated(0, vOffset);
switch (tp) {
case QStyleOptionTab::Beginning:
tdi.position = kHIThemeTabPositionFirst;
if (sp != QStyleOptionTab::NextIsSelected || stretchTabs)
tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
// Pressed state hack: tweak adjustments in preparation for flip below
if (!isSelected && tabDirection == QMacStylePrivate::West)
frameRect = frameRect.adjusted(-innerAdjust, 0, outterAdjust, 0);
else
frameRect = frameRect.adjusted(-outterAdjust, 0, innerAdjust, 0);
break;
case QStyleOptionTab::Middle:
tdi.position = kHIThemeTabPositionMiddle;
if (selected)
tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
if (sp != QStyleOptionTab::NextIsSelected || stretchTabs) // Also when we're selected.
tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0);
break;
case QStyleOptionTab::End:
tdi.position = kHIThemeTabPositionLast;
if (selected)
tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
// Pressed state hack: tweak adjustments in preparation for flip below
if (isSelected || tabDirection == QMacStylePrivate::West)
frameRect = frameRect.adjusted(-innerAdjust, 0, outterAdjust, 0);
else
frameRect = frameRect.adjusted(-outterAdjust, 0, innerAdjust, 0);
break;
case QStyleOptionTab::OnlyOneTab:
tdi.position = kHIThemeTabPositionOnly;
frameRect = frameRect.adjusted(-outterAdjust, 0, outterAdjust, 0);
break;
}
// HITheme doesn't stretch its tabs. Therefore we have to cheat and do the job ourselves.
if (stretchTabs) {
CGRect cgRect = CGRectMake(0, 0, 23, 23);
QPixmap pm(23, 23);
pm.fill(Qt::transparent);
{
QMacCGContext pmcg(&pm);
HIThemeDrawTab(&cgRect, &tdi, pmcg, kHIThemeOrientationNormal, 0);
pb.frame = frameRect.toCGRect();
pb.enabled = isEnabled;
[pb highlight:isPressed];
pb.state = isSelected && !isPressed ? NSOnState : NSOffState;
d->drawNSViewInRect(cw, pb, frameRect, p, true, ^(CGContextRef ctx, const CGRect &r) {
CGContextClipToRect(ctx, opt->rect.toCGRect());
if (!isSelected) {
// Final stage of the pressed state hack: flip NSPopupButton rendering
if (!verticalTabs && tp == QStyleOptionTab::End) {
CGContextTranslateCTM(ctx, opt->rect.right(), 0);
CGContextScaleCTM(ctx, -1, 1);
CGContextTranslateCTM(ctx, -frameRect.left(), 0);
} else if (tabDirection == QMacStylePrivate::West && tp == QStyleOptionTab::Beginning) {
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -frameRect.right());
} else if (tabDirection == QMacStylePrivate::East && tp == QStyleOptionTab::End) {
CGContextTranslateCTM(ctx, 0, opt->rect.bottom());
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -frameRect.left());
}
}
// Rotate and translate CTM when vertical
// On macOS: positive angle is CW, negative is CCW
if (tabDirection == QMacStylePrivate::West) {
CGContextTranslateCTM(ctx, 0, frameRect.right());
CGContextRotateCTM(ctx, -M_PI_2);
CGContextTranslateCTM(ctx, -frameRect.left(), 0);
} else if (tabDirection == QMacStylePrivate::East) {
CGContextTranslateCTM(ctx, opt->rect.right(), 0);
CGContextRotateCTM(ctx, M_PI_2);
}
[pb.cell drawBezelWithFrame:r inView:pb.superview];
});
if (!isSelected && sp != QStyleOptionTab::NextIsSelected
&& tp != QStyleOptionTab::End
&& tp != QStyleOptionTab::OnlyOneTab) {
static const QPen separatorPen(Qt::black, 1.0);
p->save();
p->setOpacity(isEnabled ? 0.105 : 0.06); // As measured
p->setPen(separatorPen);
if (tabDirection == QMacStylePrivate::West) {
p->drawLine(QLineF(opt->rect.left() + 1.5, opt->rect.bottom(),
opt->rect.right() - 0.5, opt->rect.bottom()));
} else if (tabDirection == QMacStylePrivate::East) {
p->drawLine(QLineF(opt->rect.left(), opt->rect.bottom(),
opt->rect.right() - 0.5, opt->rect.bottom()));
} else {
p->drawLine(QLineF(opt->rect.right(), opt->rect.top() + 1.0,
opt->rect.right(), opt->rect.bottom() - 0.5));
}
p->restore();
}
// TODO Needs size adjustment to fit the focus ring
if (tabOpt->state & State_HasFocus) {
QMacStylePrivate::CocoaControlType focusRingType;
switch (tp) {
case QStyleOptionTab::Beginning:
focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_Last
: QMacStylePrivate::SegmentedControl_First;
break;
case QStyleOptionTab::Middle:
focusRingType = QMacStylePrivate::SegmentedControl_Middle;
break;
case QStyleOptionTab::End:
focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_First
: QMacStylePrivate::SegmentedControl_Last;
break;
case QStyleOptionTab::OnlyOneTab:
focusRingType = QMacStylePrivate::SegmentedControl_Single;
break;
}
QStyleHelper::drawBorderPixmap(pm, p, tabRect, 7, 7, 7, 7);
} else {
CGRect cgRect = tabRect.toCGRect();
HIThemeDrawTab(&cgRect, &tdi, cg, kHIThemeOrientationNormal, 0);
}
}
break;
case CE_TabBarTabLabel:
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
QStyleOptionTab myTab = *tab;
const bool verticalTabs = tab->shape == QTabBar::RoundedWest
|| tab->shape == QTabBar::RoundedEast
|| tab->shape == QTabBar::TriangularWest
|| tab->shape == QTabBar::TriangularEast;
const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape);
const bool verticalTabs = tabDirection == QMacStylePrivate::East
|| tabDirection == QMacStylePrivate::West;
// Check to see if we use have the same as the system font
// (QComboMenuItem is internal and should never be seen by the
@ -5951,18 +6006,17 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
#if QT_CONFIG(tabbar)
case QStyle::CT_TabBarTab:
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
const QStyleHelper::WidgetSizePolicy AquaSize = d->aquaSizeConstrain(opt, widget);
const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont))
|| !QApplication::desktopSettingsAware();
ThemeTabDirection ttd = (ThemeTabDirection)QMacStylePrivate::tabDirection(tab->shape);
bool vertTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
if (vertTabs)
const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape);
const bool verticalTabs = tabDirection == QMacStylePrivate::East
|| tabDirection == QMacStylePrivate::West;
if (verticalTabs)
sz = sz.transposed();
int defaultTabHeight;
int extraHSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget);
QFontMetrics fm = opt->fontMetrics;
switch (AquaSize) {
case QStyleHelper::SizeDefault:
const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
switch (cs) {
case QStyleHelper::SizeLarge:
if (tab->documentMode)
defaultTabHeight = 24;
@ -5975,19 +6029,21 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
case QStyleHelper::SizeMini:
defaultTabHeight = 16;
break;
default:
break;
}
bool setWidth = false;
if (differentFont || !tab->icon.isNull()) {
sz.rheight() = qMax(defaultTabHeight, sz.height());
} else {
QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
sz.rheight() = qMax(defaultTabHeight, textSize.height());
sz.rwidth() = textSize.width();
setWidth = true;
}
sz.rwidth() += extraHSpace;
if (vertTabs)
const bool widthSet = !differentFont && tab->icon.isNull();
if (widthSet) {
const auto textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, tab->text);
sz.rwidth() = textSize.width();
sz.rheight() = qMax(defaultTabHeight, textSize.height());
} else {
sz.rheight() = qMax(defaultTabHeight, sz.height());
}
sz.rwidth() += proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget);
if (verticalTabs)
sz = sz.transposed();
int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height());
@ -6007,11 +6063,11 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
widgetHeight += tab->rightButtonSize.height();
}
if (vertTabs) {
sz.setHeight(sz.height() + widgetHeight + padding);
if (verticalTabs) {
sz.setWidth(qMax(sz.width(), maxWidgetWidth));
sz.setHeight(sz.height() + widgetHeight + padding);
} else {
if (setWidth)
if (widthSet)
sz.setWidth(sz.width() + widgetWidth + padding);
sz.setHeight(qMax(sz.height(), maxWidgetHeight));
}

View File

@ -192,7 +192,7 @@ public:
Button_Disclosure, // Disclosure triangle, like in QTreeView
Button_PopupButton, // Non-editable QComboBox
Button_PullDown, // QPushButton with menu
Button_PushButton,
Button_PushButton, // Plain QPushButton and QTabBar buttons
Button_RadioButton,
Button_SquareButton, // Oversized QPushButton
Button_WindowClose,
@ -203,6 +203,10 @@ public:
ProgressIndicator_Indeterminate,
Scroller_Horizontal,
Scroller_Vertical,
SegmentedControl_First, // QTabBar buttons focus ring
SegmentedControl_Middle,
SegmentedControl_Last,
SegmentedControl_Single,
Slider_Horizontal,
Slider_Vertical,
SplitView_Horizontal,