diff options
Diffstat (limited to 'src/plugins/styles/mac/qmacstyle_mac.mm')
-rw-r--r-- | src/plugins/styles/mac/qmacstyle_mac.mm | 648 |
1 files changed, 287 insertions, 361 deletions
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 16762cf6d6..05585ba07c 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -74,10 +74,18 @@ #include <qgroupbox.h> #include <qhash.h> #include <qheaderview.h> +#if QT_CONFIG(lineedit) #include <qlineedit.h> +#endif +#if QT_CONFIG(mainwindow) #include <qmainwindow.h> +#endif +#if QT_CONFIG(mdiarea) #include <qmdisubwindow.h> +#endif +#if QT_CONFIG(menubar) #include <qmenubar.h> +#endif #include <qpaintdevice.h> #include <qpainter.h> #include <qpixmapcache.h> @@ -95,7 +103,9 @@ #if QT_CONFIG(scrollbar) #include <qscrollbar.h> #endif +#if QT_CONFIG(sizegrip) #include <qsizegrip.h> +#endif #include <qstyleoption.h> #include <qtoolbar.h> #if QT_CONFIG(toolbutton) @@ -327,6 +337,40 @@ static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb) return true; } +static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) +{ + if (sl->minimum >= sl->maximum) + return false; + + slider.frame = sl->rect.toCGRect(); + slider.minValue = sl->minimum; + slider.maxValue = sl->maximum; + slider.intValue = sl->sliderPosition; + slider.enabled = sl->state & QStyle::State_Enabled; + if (sl->tickPosition != QSlider::NoTicks) { + // Set numberOfTickMarks, but TicksBothSides will be treated differently + int interval = sl->tickInterval; + if (interval == 0) { + interval = sl->pageStep; + if (interval == 0) + interval = sl->singleStep; + if (interval == 0) + interval = 1; // return false? + } + slider.numberOfTickMarks = 1 + ((sl->maximum - sl->minimum) / interval); + + const bool ticksAbove = sl->tickPosition == QSlider::TicksAbove; + if (sl->orientation == Qt::Horizontal) + slider.tickMarkPosition = ticksAbove ? NSTickMarkAbove : NSTickMarkBelow; + else + slider.tickMarkPosition = ticksAbove ? NSTickMarkLeft : NSTickMarkRight; + } else { + slider.numberOfTickMarks = 0; + } + + return true; +} + static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY) { QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); @@ -656,7 +700,6 @@ const int macItemFrame = 2; // menu item frame width const int macItemHMargin = 3; // menu item hor text margin const int macRightBorder = 12; // right border on mac const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar. -QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background. /***************************************************************************** QMacCGStyle utility functions @@ -799,17 +842,17 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg else if (qobject_cast<const QProgressBar *>(widg)) ct = QStyle::CT_ProgressBar; #endif -#ifndef QT_NO_LINEEDIT +#if QT_CONFIG(lineedit) else if (qobject_cast<const QLineEdit *>(widg)) ct = QStyle::CT_LineEdit; #endif else if (qobject_cast<const QHeaderView *>(widg)) ct = QStyle::CT_HeaderSection; -#ifndef QT_NO_MENUBAR +#if QT_CONFIG(menubar) else if (qobject_cast<const QMenuBar *>(widg)) ct = QStyle::CT_MenuBar; #endif -#ifndef QT_NO_SIZEGRIP +#if QT_CONFIG(sizegrip) else if (qobject_cast<const QSizeGrip *>(widg)) ct = QStyle::CT_SizeGrip; #endif @@ -897,7 +940,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg gbi.size = sz == QStyleHelper::SizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal; if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr) { int width = 0; -#ifndef QT_NO_MDIAREA +#if QT_CONFIG(mdiarea) if (widg && qobject_cast<QMdiSubWindow *>(widg->parentWidget())) width = r.size.width; #endif @@ -1085,7 +1128,7 @@ static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size(const QWidget *widg, QS return QStyleHelper::SizeLarge; } -#ifndef QT_NO_MAINWINDOW +#if QT_CONFIG(mainwindow) if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) { //if (small.width() != -1 || small.height() != -1) return QStyleHelper::SizeSmall; @@ -1732,7 +1775,7 @@ void QMacStylePrivate::drawCombobox(const CGRect &outerBounds, const HIThemeButt void QMacStylePrivate::drawTableHeader(const CGRect &outerBounds, bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p) { - static SInt32 headerHeight = qt_mac_aqua_get_metric(ListHeaderHeight); + static int headerHeight = qt_mac_aqua_get_metric(ListHeaderHeight); QPixmap buffer; QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value)); @@ -1788,69 +1831,6 @@ void QMacStylePrivate::drawTableHeader(const CGRect &outerBounds, p->translate(-outerBounds.origin.x, -outerBounds.origin.y); } -void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider, - HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe) const -{ - Q_UNUSED(cc); - memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another... - tdi->version = qt_mac_hitheme_version; - tdi->reserved = 0; - tdi->filler1 = 0; - switch (aquaSizeConstrain(slider, needToRemoveMe)) { - case QStyleHelper::SizeDefault: - case QStyleHelper::SizeLarge: - tdi->kind = kThemeMediumSlider; - break; - case QStyleHelper::SizeMini: - tdi->kind = kThemeMiniSlider; - break; - case QStyleHelper::SizeSmall: - tdi->kind = kThemeSmallSlider; - break; - } - - bool usePlainKnob = slider->tickPosition == QSlider::NoTicks - || slider->tickPosition == QSlider::TicksBothSides; - - tdi->bounds = slider->rect.toCGRect(); - // Fix min and max positions. HITheme seems confused when it comes to rendering - // a slider at those positions. We give it a hand by extending and offsetting - // the slider range accordingly. See also comment for CC_Slider in drawComplexControl() - tdi->min = 0; - if (slider->orientation == Qt::Horizontal) - tdi->max = 10 * slider->rect.width(); - else - tdi->max = 10 * slider->rect.height(); - - int range = slider->maximum - slider->minimum; - if (range == 0) { - tdi->value = 0; - } else if (usePlainKnob || slider->orientation == Qt::Horizontal) { - int endsCorrection = usePlainKnob ? 25 : 10; - tdi->value = (tdi->max + 2 * endsCorrection) * (slider->sliderPosition - slider->minimum) / range - endsCorrection; - } else { - tdi->value = (tdi->max + 30) * (slider->sliderPosition - slider->minimum) / range - 20; - } - - tdi->attributes = kThemeTrackShowThumb; - if (slider->upsideDown) - tdi->attributes |= kThemeTrackRightToLeft; - if (slider->orientation == Qt::Horizontal) { - tdi->attributes |= kThemeTrackHorizontal; - } - - tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive - : kThemeTrackDisabled; - if (slider->state & QStyle::QStyle::State_HasFocus) - tdi->attributes |= kThemeTrackHasFocus; - if (usePlainKnob) - tdi->trackInfo.slider.thumbDir = kThemeThumbPlain; - else if (slider->tickPosition == QSlider::TicksAbove) - tdi->trackInfo.slider.thumbDir = kThemeThumbUpward; - else - tdi->trackInfo.slider.thumbDir = kThemeThumbDownward; -} - QMacStylePrivate::QMacStylePrivate() : backingStoreNSView(nil) { @@ -1963,11 +1943,11 @@ NSView *QMacStylePrivate::cocoaControl(QCocoaWidget widget) const // at construction time, and it cannot be changed later. bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; else if (widget.first == QCocoaHorizontalSlider) - bv = [[NSSlider alloc] init]; + bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)]; else if (widget.first == QCocoaVerticalSlider) // Cocoa sets the orientation from the view's frame // at construction time, and it cannot be changed later. - bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 10, 100)]; + bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; else bv = [[NSButton alloc] init]; @@ -2289,79 +2269,10 @@ QMacStyle::~QMacStyle() [[NSNotificationCenter defaultCenter] removeObserver:d->receiver]; [d->receiver release]; - - delete qt_mac_backgroundPattern; - qt_mac_backgroundPattern = 0; } -/*! \internal - Generates the standard widget background pattern. -*/ -QPixmap QMacStylePrivate::generateBackgroundPattern() const +void QMacStyle::polish(QPalette &) { - QMacAutoReleasePool pool; - QPixmap px(4, 4); - QMacCGContext cg(&px); - HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal); - const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height()); - CGContextFillRect(cg, cgRect); - return px; -} - -/*! \internal - Fills the given \a rect with the pattern stored in \a brush. As an optimization, - HIThemeSetFill us used directly if we are filling with the standard background. -*/ -void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush) -{ -#if 0 - QPoint dummy; - const QPaintDevice *target = painter->device(); - const QPaintDevice *redirected = QPainter::redirected(target, &dummy); - //const bool usePainter = redirected && redirected != target; - - if (!usePainter && qt_mac_backgroundPattern - && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) { - - painter->setClipRegion(rgn); - - QMacCGContext cg(target); - CGContextSaveGState(cg); - HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted); - - for (const QRect &rect : rgn) { - // Anchor the pattern to the top so it stays put when the window is resized. - CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height())); - CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); - CGContextFillRect(cg, mac_rect); - } - - CGContextRestoreGState(cg); - } else { -#endif - const QRect rect(rgn.boundingRect()); - painter->setClipRegion(rgn); - painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); -// } -} - -void QMacStyle::polish(QPalette &pal) -{ - Q_D(QMacStyle); - if (!qt_mac_backgroundPattern) { - if (!qApp) - return; - qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern()); - } - - - QCFString theme; - const OSErr err = CopyThemeIdentifier(&theme); - if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) { - pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240)); - } else { - pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254)); - } } void QMacStyle::polish(QApplication *) @@ -2374,7 +2285,7 @@ void QMacStyle::unpolish(QApplication *) void QMacStyle::polish(QWidget* w) { -#ifndef QT_NO_MENU +#if QT_CONFIG(menu) if (qobject_cast<QMenu*>(w) #if QT_CONFIG(combobox) || qobject_cast<QComboBoxPrivateContainer *>(w) @@ -2433,7 +2344,7 @@ void QMacStyle::polish(QWidget* w) void QMacStyle::unpolish(QWidget* w) { if ( -#ifndef QT_NO_MENU +#if QT_CONFIG(menu) qobject_cast<QMenu*>(w) && #endif !w->testAttribute(Qt::WA_SetPalette)) { @@ -2483,7 +2394,7 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW { Q_D(const QMacStyle); const int controlSize = getControlSize(opt, widget); - SInt32 ret = 0; + int ret = 0; switch (metric) { case PM_TabCloseIndicatorWidth: @@ -2541,7 +2452,7 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW ret = 15; // I hate having magic numbers in here... break; case PM_DefaultFrameWidth: -#ifndef QT_NO_MAINWINDOW +#if QT_CONFIG(mainwindow) if (widget && (widget->isWindow() || !widget->parentWidget() || (qobject_cast<const QMainWindow*>(widget->parentWidget()) && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget)) @@ -2679,7 +2590,7 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW break; case PM_ScrollBarExtent: { const QStyleHelper::WidgetSizePolicy size = d->effectiveAquaSizeConstrain(opt, widget); - ret = static_cast<SInt32>([NSScroller + ret = static_cast<int>([NSScroller scrollerWidthForControlSize:static_cast<NSControlSize>(size) scrollerStyle:[NSScroller preferredScrollerStyle]]); break; } @@ -2861,7 +2772,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w { QMacAutoReleasePool pool; - SInt32 ret = 0; + int ret = 0; switch (sh) { case SH_Slider_SnapToValue: case SH_PrintDialog_RightAlignButtons: @@ -3154,7 +3065,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w opt->rect.width(), opt->rect.height() - 8); HIThemeMenuDrawInfo mdi; mdi.version = 0; -#ifndef QT_NO_MENU +#if QT_CONFIG(menu) if (w && qobject_cast<QMenu *>(w->parentWidget())) mdi.menuType = kThemeMenuTypeHierarchical; else @@ -3570,7 +3481,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai HIThemeFrameDrawInfo fdi; fdi.version = qt_mac_hitheme_version; fdi.state = tds; - SInt32 frame_size; + int frame_size; fdi.kind = frame->features & QStyleOptionFrame::Rounded ? kHIThemeFrameTextFieldRound : kHIThemeFrameTextFieldSquare; frame_size = qt_mac_aqua_get_metric(EditTextFrameOutset); @@ -3605,7 +3516,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit). // Focus frame is drawn outside the rectangle passed in the option-rect. if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { -#ifndef QT_NO_LINEEDIT +#if QT_CONFIG(lineedit) if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) { int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin); int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin); @@ -4683,7 +4594,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // the title bar. The following code fills the toolBar area with transparent pixels // to make that gradient visible. if (w) { -#ifndef QT_NO_MAINWINDOW +#if QT_CONFIG(mainwindow) if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) { if (toolBar && toolBar->toolBarArea == Qt::TopToolBarArea && mainWindow->unifiedTitleAndToolBarOnMac()) { @@ -4700,7 +4611,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QPoint windowToolbarEnd = w->mapTo(w->window(), opt->rect.bottomLeft()); bool isEndOfUnifiedArea = !isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowToolbarEnd.y() + 1); if (isEndOfUnifiedArea) { - SInt32 margin; + int margin; margin = qt_mac_aqua_get_metric(SeparatorSize); CGRect separatorRect = CGRectMake(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin); HIThemeSeparatorDrawInfo separatorDrawInfo; @@ -5271,6 +5182,11 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex case CC_ScrollBar: if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool drawTrack = sb->subControls & SC_ScrollBarGroove; + const bool drawKnob = sb->subControls & SC_ScrollBarSlider; + if (!drawTrack && !drawKnob) + break; + const bool isHorizontal = sb->orientation == Qt::Horizontal; if (opt && opt->styleObject && !QMacStylePrivate::scrollBars.contains(opt->styleObject)) @@ -5390,49 +5306,53 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex CGContextSetAlpha(cg, opacity); } - // Draw the track when hovering. Expand by shifting the track rect. - if (!isTransient || opt->activeSubControls || wasActive) { - CGRect trackRect = scroller.bounds; - if (isHorizontal) - trackRect.origin.y += expandOffset; - else - trackRect.origin.x += expandOffset; - [scroller drawKnobSlotInRect:trackRect highlight:NO]; + if (drawTrack) { + // Draw the track when hovering. Expand by shifting the track rect. + if (!isTransient || opt->activeSubControls || wasActive) { + CGRect trackRect = scroller.bounds; + if (isHorizontal) + trackRect.origin.y += expandOffset; + else + trackRect.origin.x += expandOffset; + [scroller drawKnobSlotInRect:trackRect highlight:NO]; + } } - if (shouldExpand) { - // -[NSScroller drawKnob] is not useful here because any scaling applied - // will only be used to draw the hi-DPI artwork. And even if did scale, - // the stretched knob would look wrong, actually. So we need to draw the - // scroller manually when it's being hovered. - const CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:scroller.controlSize scrollerStyle:scroller.scrollerStyle]; - const CGFloat knobWidth = knobWidths[cocoaSize] * expandScale; - // Cocoa can help get the exact knob length in the current orientation - const CGRect scrollerKnobRect = CGRectInset([scroller rectForPart:NSScrollerKnob], 1, 1); - const CGFloat knobLength = isHorizontal ? scrollerKnobRect.size.width : scrollerKnobRect.size.height; - const CGFloat knobPos = isHorizontal ? scrollerKnobRect.origin.x : scrollerKnobRect.origin.y; - const CGFloat knobOffset = qRound((scrollerWidth + expandOffset - knobWidth) / 2.0); - const CGFloat knobRadius = knobWidth / 2.0; - CGRect knobRect; - if (isHorizontal) - knobRect = CGRectMake(knobPos, knobOffset, knobLength, knobWidth); - else - knobRect = CGRectMake(knobOffset, knobPos, knobWidth, knobLength); - QCFType<CGPathRef> knobPath = CGPathCreateWithRoundedRect(knobRect, knobRadius, knobRadius, nullptr); - CGContextAddPath(cg, knobPath); - CGContextSetAlpha(cg, 0.5); - CGContextSetFillColorWithColor(cg, NSColor.blackColor.CGColor); - CGContextFillPath(cg); - } else { - [scroller drawKnob]; - - if (!isTransient && opt->activeSubControls) { - // The knob should appear darker (going from 0.76 down to 0.49). - // But no blending mode can help darken enough in a single pass, - // so we resort to drawing the knob twice with a small help from - // blending. This brings the gray level to a close enough 0.53. - CGContextSetBlendMode(cg, kCGBlendModePlusDarker); + if (drawKnob) { + if (shouldExpand) { + // -[NSScroller drawKnob] is not useful here because any scaling applied + // will only be used to draw the hi-DPI artwork. And even if did scale, + // the stretched knob would look wrong, actually. So we need to draw the + // scroller manually when it's being hovered. + const CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:scroller.controlSize scrollerStyle:scroller.scrollerStyle]; + const CGFloat knobWidth = knobWidths[cocoaSize] * expandScale; + // Cocoa can help get the exact knob length in the current orientation + const CGRect scrollerKnobRect = CGRectInset([scroller rectForPart:NSScrollerKnob], 1, 1); + const CGFloat knobLength = isHorizontal ? scrollerKnobRect.size.width : scrollerKnobRect.size.height; + const CGFloat knobPos = isHorizontal ? scrollerKnobRect.origin.x : scrollerKnobRect.origin.y; + const CGFloat knobOffset = qRound((scrollerWidth + expandOffset - knobWidth) / 2.0); + const CGFloat knobRadius = knobWidth / 2.0; + CGRect knobRect; + if (isHorizontal) + knobRect = CGRectMake(knobPos, knobOffset, knobLength, knobWidth); + else + knobRect = CGRectMake(knobOffset, knobPos, knobWidth, knobLength); + QCFType<CGPathRef> knobPath = CGPathCreateWithRoundedRect(knobRect, knobRadius, knobRadius, nullptr); + CGContextAddPath(cg, knobPath); + CGContextSetAlpha(cg, 0.5); + CGContextSetFillColorWithColor(cg, NSColor.blackColor.CGColor); + CGContextFillPath(cg); + } else { [scroller drawKnob]; + + if (!isTransient && opt->activeSubControls) { + // The knob should appear darker (going from 0.76 down to 0.49). + // But no blending mode can help darken enough in a single pass, + // so we resort to drawing the knob twice with a small help from + // blending. This brings the gray level to a close enough 0.53. + CGContextSetBlendMode(cg, kCGBlendModePlusDarker); + [scroller drawKnob]; + } } } @@ -5443,152 +5363,124 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } break; case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - const bool isHorizontal = slider->orientation == Qt::Horizontal; + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto cs = d->effectiveAquaSizeConstrain(opt, widget); + const auto cw = QCocoaWidget(isHorizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + const bool hasTicks = sl->tickPosition != QSlider::NoTicks; + const bool hasDoubleTicks = sl->tickPosition == QSlider::TicksBothSides; + const bool drawKnob = sl->subControls & SC_SliderHandle; + const bool drawBar = sl->subControls & SC_SliderGroove; + const bool drawTicks = sl->subControls & SC_SliderTickmarks; + const bool isPressed = sl->state & State_Sunken; + + CGPoint pressPoint; + if (isPressed) { + const CGRect knobRect = [slider.cell knobRectFlipped:NO]; + pressPoint.x = CGRectGetMidX(knobRect); + pressPoint.y = CGRectGetMidY(knobRect); + [slider.cell startTrackingAt:pressPoint inView:slider]; + } - HIThemeTrackDrawInfo tdi; - d->getSliderInfo(cc, slider, &tdi, widget); - if (slider->state & State_Sunken) { - if (cc == CC_Slider) { - if (slider->activeSubControls == SC_SliderHandle) - tdi.trackInfo.slider.pressState = kThemeThumbPressed; - else if (slider->activeSubControls == SC_SliderGroove) - tdi.trackInfo.slider.pressState = kThemeLeftTrackPressed; + d->drawNSViewInRect(cw, slider, opt->rect, p, widget != 0, ^(CGContextRef ctx, const CGRect &rect) { + if (isHorizontal && sl->upsideDown) { + CGContextTranslateCTM(ctx, rect.size.width, 0); + CGContextScaleCTM(ctx, -1, 1); } - } - CGRect macRect; - bool tracking = slider->sliderPosition == slider->sliderValue; - if (!tracking) { - // Small optimization, the same as q->subControlRect - QCFType<HIShapeRef> shape; - HIThemeGetTrackThumbShape(&tdi, &shape); - HIShapeGetBounds(shape, &macRect); - tdi.value = slider->sliderValue; - } - if (!(slider->subControls & SC_SliderHandle)) - tdi.attributes &= ~kThemeTrackShowThumb; - if (!(slider->subControls & SC_SliderGroove)) - tdi.attributes |= kThemeTrackHideTrack; - - // Fix min and max positions. (See also getSliderInfo() - // for the slider values adjustments.) - // HITheme seems to have forgotten how to render - // a slide at those positions, leaving a gap between - // the knob and the ends of the track. - // We fix this by rendering the track first, and then - // the knob on top. However, in order to not clip the - // knob, we reduce the the drawing rect for the track. - CGRect bounds = tdi.bounds; - if (isHorizontal) { - tdi.bounds.size.width -= 2; - tdi.bounds.origin.x += 1; - if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward) - tdi.bounds.origin.y -= 2; - else if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) - tdi.bounds.origin.y += 3; - } else { - tdi.bounds.size.height -= 2; - tdi.bounds.origin.y += 1; - if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward) // pointing right - tdi.bounds.origin.x -= 4; - else if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) // pointing left - tdi.bounds.origin.x += 2; - } + if (hasDoubleTicks) { + // This ain't HIG kosher: eye-proved constants + if (isHorizontal) + CGContextTranslateCTM(ctx, 0, 4); + else + CGContextTranslateCTM(ctx, 1, 0); + } - // Yosemite demands its blue progress track when no tickmarks are present - if (!(slider->subControls & SC_SliderTickmarks)) { - QCocoaWidgetKind sliderKind = slider->orientation == Qt::Horizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider; - QCocoaWidget cw = QCocoaWidget(sliderKind, QStyleHelper::SizeLarge); - NSSlider *sl = (NSSlider *)d->cocoaControl(cw); - sl.minValue = slider->minimum; - sl.maxValue = slider->maximum; - sl.intValue = slider->sliderValue; - sl.enabled = slider->state & QStyle::State_Enabled; - d->drawNSViewInRect(cw, sl, opt->rect, p, widget != 0, ^(CGContextRef ctx, const CGRect &rect) { - const bool isSierraOrLater = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra; - if (slider->upsideDown) { - if (isHorizontal) { - CGContextTranslateCTM(ctx, rect.size.width, 0); - CGContextScaleCTM(ctx, -1, 1); - } - } else if (!isHorizontal && !isSierraOrLater) { - CGContextTranslateCTM(ctx, 0, rect.size.height); - CGContextScaleCTM(ctx, 1, -1); - } - const bool shouldFlip = isHorizontal || (slider->upsideDown && isSierraOrLater); - [sl.cell drawBarInside:NSRectFromCGRect(tdi.bounds) flipped:shouldFlip]; - // No need to restore the CTM later, the context has been saved - // and will be restored at the end of drawNSViewInRect() - }); - tdi.attributes |= kThemeTrackHideTrack; - - tdi.bounds = bounds; - } + // Since the GC is flipped, upsideDown means *not* inverted when vertical. + const bool verticalFlip = !isHorizontal && !sl->upsideDown; // FIXME: && !isSierraOrLater - if (slider->subControls & SC_SliderTickmarks) { - - CGRect bounds; - // As part of fixing the min and max positions, - // we need to adjust the tickmarks as well - bounds = tdi.bounds; - if (slider->orientation == Qt::Horizontal) { - tdi.bounds.size.width += 2; - tdi.bounds.origin.x -= 1; - if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) - tdi.bounds.origin.y -= 2; - } else { - tdi.bounds.size.height += 3; - tdi.bounds.origin.y -= 3; - tdi.bounds.origin.y += 1; - if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) // pointing left - tdi.bounds.origin.x -= 2; - } +#if 0 + // FIXME: Sadly, this part doesn't work. It seems to somehow polute the + // NSSlider's internal state and, when we need to use the "else" part, + // the slider's frame is not in sync with its cell dimensions. + const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks); + if (drawAllParts && !hasDoubleTicks && (!verticalFlip || drawTicks)) { + // Draw eveything at once if we're going to, except for inverted vertical + // sliders which need to be drawn part by part because of the shadow below + // the knob. Same for two-sided tickmarks. + if (verticalFlip && drawTicks) { + // Since tickmarks are always rendered symmetrically, a vertically + // flipped slider with tickmarks only needs to get its value flipped. + slider.intValue = slider.maxValue - slider.intValue + slider.minValue; + } + [slider drawRect:CGRectZero]; + } else +#endif + { + [slider calcSize]; + NSSliderCell *cell = slider.cell; + + const int numberOfTickMarks = slider.numberOfTickMarks; + // This ain't HIG kosher: force tick-less bar position. + if (hasDoubleTicks) + slider.numberOfTickMarks = 0; + + const CGRect barRect = [cell barRectFlipped:hasTicks]; + if (drawBar) { + // This ain't HIG kosher: force unfilled bar look. + if (hasDoubleTicks) + slider.numberOfTickMarks = numberOfTickMarks; + [cell drawBarInside:barRect flipped:!verticalFlip]; + } - int interval = slider->tickInterval; - if (interval == 0) { - interval = slider->pageStep; - if (interval == 0) - interval = slider->singleStep; - if (interval == 0) - interval = 1; - } - int numMarks = 1 + ((slider->maximum - slider->minimum) / interval); - - if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) { - // They asked for both, so we'll give it to them. - tdi.trackInfo.slider.thumbDir = kThemeThumbDownward; - HIThemeDrawTrackTickMarks(&tdi, numMarks, - cg, - kHIThemeOrientationNormal); - tdi.trackInfo.slider.thumbDir = kThemeThumbUpward; - // 10.10 and above need a slight shift - if (slider->orientation == Qt::Vertical) - tdi.bounds.origin.x -= 2; - HIThemeDrawTrackTickMarks(&tdi, numMarks, - cg, - kHIThemeOrientationNormal); - // Reset to plain thumb to be drawn further down - tdi.trackInfo.slider.thumbDir = kThemeThumbPlain; - } else { - HIThemeDrawTrackTickMarks(&tdi, numMarks, - cg, - kHIThemeOrientationNormal); - } + if (hasTicks && drawTicks) { + if (!drawBar && hasDoubleTicks) + slider.numberOfTickMarks = numberOfTickMarks; - tdi.bounds = bounds; - } + [cell drawTickMarks]; - HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg, - kHIThemeOrientationNormal); + if (hasDoubleTicks) { + // This ain't HIG kosher: just slap a set of tickmarks on each side, like we used to. + CGAffineTransform tickMarksFlip; + const CGRect tickMarkRect = [cell rectOfTickMarkAtIndex:0]; + if (isHorizontal) { + tickMarksFlip = CGAffineTransformMakeTranslation(0, rect.size.height - tickMarkRect.size.height - 3); + tickMarksFlip = CGAffineTransformScale(tickMarksFlip, 1, -1); + } else { + tickMarksFlip = CGAffineTransformMakeTranslation(rect.size.width - tickMarkRect.size.width / 2, 0); + tickMarksFlip = CGAffineTransformScale(tickMarksFlip, -1, 1); + } + CGContextConcatCTM(ctx, tickMarksFlip); + [cell drawTickMarks]; + CGContextConcatCTM(ctx, CGAffineTransformInvert(tickMarksFlip)); + } + } + + if (drawKnob) { + // This ain't HIG kosher: force round knob look. + if (hasDoubleTicks) + slider.numberOfTickMarks = 0; + // Draw the knob in the symmetrical position instead of flipping. + if (verticalFlip) + slider.intValue = slider.maxValue - slider.intValue + slider.minValue; + [cell drawKnob]; + } + } + }); + + if (isPressed) + [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; } break; -#ifndef QT_NO_SPINBOX +#if QT_CONFIG(spinbox) case CC_SpinBox: if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { - SInt32 frame_size; + int frame_size; frame_size = qt_mac_aqua_get_metric(EditTextFrameOutset); QRect lineeditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget); @@ -5618,15 +5510,16 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const bool downPressed = sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken); const CGFloat x = CGRectGetMidX(newRect); const CGFloat y = upPressed ? -3 : 3; // FIXME Weird coordinate shift going on + const CGPoint pressPoint = CGPointMake(x, y); // Pretend we're pressing the mouse on the right button. Unfortunately, NSStepperCell has no // API to highlight a specific button. The highlighted property works only on the down button. if (upPressed || downPressed) - [cell startTrackingAt:CGPointMake(x, y) inView:d->backingStoreNSView]; + [cell startTrackingAt:pressPoint inView:d->backingStoreNSView]; [cell drawWithFrame:newRect inView:d->backingStoreNSView]; if (upPressed || downPressed) - [cell stopTracking:CGPointMake(x, y) at:CGPointMake(x, y) inView:d->backingStoreNSView mouseIsUp:NO]; + [cell stopTracking:pressPoint at:pressPoint inView:d->backingStoreNSView mouseIsUp:NO]; d->restoreNSGraphicsContext(cg); } @@ -5941,16 +5834,28 @@ QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc, } break; case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - HIThemeTrackDrawInfo tdi; - d->getSliderInfo(cc, slider, &tdi, widget); - ControlPartCode part; - CGPoint pos = CGPointMake(pt.x(), pt.y()); - if (HIThemeHitTestTrack(&tdi, &pos, &part)) { - if (part == kControlPageUpPart || part == kControlPageDownPart) - sc = SC_SliderGroove; - else - sc = SC_SliderHandle; + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + if (!sl->rect.contains(pt)) + break; + + const bool hasTicks = sl->tickPosition != QSlider::NoTicks; + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto cs = d->effectiveAquaSizeConstrain(opt, widget); + const auto cw = QCocoaWidget(isHorizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + + [slider calcSize]; + NSSliderCell *cell = slider.cell; + const auto barRect = QRectF::fromCGRect([cell barRectFlipped:hasTicks]); + const auto knobRect = QRectF::fromCGRect([cell knobRectFlipped:NO]); + if (knobRect.contains(pt)) { + sc = SC_SliderHandle; + } else if (barRect.contains(pt)) { + sc = SC_SliderGroove; + } else if (hasTicks) { + sc = SC_SliderTickmarks; } } break; @@ -6078,30 +5983,51 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op } break; case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { - HIThemeTrackDrawInfo tdi; - d->getSliderInfo(cc, slider, &tdi, widget); - CGRect macRect; - QCFType<HIShapeRef> shape; + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool hasTicks = sl->tickPosition != QSlider::NoTicks; + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto cs = d->effectiveAquaSizeConstrain(opt, widget); + const auto cw = QCocoaWidget(isHorizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + + [slider calcSize]; + NSSliderCell *cell = slider.cell; if (sc == SC_SliderHandle) { - HIThemeGetTrackThumbShape(&tdi, &shape); - HIShapeGetBounds(shape, &macRect); + ret = QRectF::fromCGRect([cell knobRectFlipped:NO]).toRect(); + if (isHorizontal) { + ret.setTop(sl->rect.top()); + ret.setBottom(sl->rect.bottom()); + } else { + ret.setLeft(sl->rect.left()); + ret.setRight(sl->rect.right()); + } } else if (sc == SC_SliderGroove) { - HIThemeGetTrackBounds(&tdi, &macRect); + ret = QRectF::fromCGRect([cell barRectFlipped:hasTicks]).toRect(); + } else if (hasTicks && sc == SC_SliderTickmarks) { + const auto tickMarkRect = QRectF::fromCGRect([cell rectOfTickMarkAtIndex:0]); + if (isHorizontal) + ret = QRect(sl->rect.left(), tickMarkRect.top(), sl->rect.width(), tickMarkRect.height()); + else + ret = QRect(tickMarkRect.left(), sl->rect.top(), tickMarkRect.width(), sl->rect.height()); } - // FIXME No SC_SliderTickmarks? - ret = QRectF::fromCGRect(macRect).toRect(); - - // Tweak: the dark line between the sub/add line buttons belong to only one of the buttons - // when doing hit-testing, but both of them have to repaint it. Extend the rect to cover - // the line in the cases where HIThemeGetTrackPartBounds returns a rect that doesn't. - if (slider->orientation == Qt::Horizontal) { - if (slider->direction == Qt::LeftToRight && sc == SC_ScrollBarSubLine) - ret.adjust(0, 0, 1, 0); - else if (slider->direction == Qt::RightToLeft && sc == SC_ScrollBarAddLine) - ret.adjust(-1, 0, 1, 0); - } else if (sc == SC_ScrollBarAddLine) { - ret.adjust(0, -1, 0, 1); + + // Invert if needed and extend to the actual bounds of the slider + if (isHorizontal) { + if (sl->upsideDown) { + ret = QRect(sl->rect.right() - ret.right(), sl->rect.top(), ret.width(), sl->rect.height()); + } else { + ret.setTop(sl->rect.top()); + ret.setBottom(sl->rect.bottom()); + } + } else { + if (!sl->upsideDown) { + ret = QRect(sl->rect.left(), sl->rect.bottom() - ret.bottom(), sl->rect.width(), ret.height()); + } else { + ret.setLeft(sl->rect.left()); + ret.setRight(sl->rect.right()); + } } } break; @@ -6274,7 +6200,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op } } break; -#ifndef QT_NO_SPINBOX +#if QT_CONFIG(spinbox) case CC_SpinBox: if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { QStyleHelper::WidgetSizePolicy aquaSize = d->effectiveAquaSizeConstrain(spin, widget); @@ -6385,7 +6311,7 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, bool useAquaGuideline = true; switch (ct) { -#ifndef QT_NO_SPINBOX +#if QT_CONFIG(spinbox) case CT_SpinBox: if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { // Add button + frame widths @@ -6749,7 +6675,7 @@ bool QMacStyle::event(QEvent *e) QWidget *top = f->parentWidget(); while (top && !top->isWindow() && !(top->windowType() == Qt::SubWindow)) top = top->parentWidget(); -#ifndef QT_NO_MAINWINDOW +#if QT_CONFIG(mainwindow) if (qobject_cast<QMainWindow *>(top)) { QWidget *central = static_cast<QMainWindow *>(top)->centralWidget(); for (const QWidget *par = f; par; par = par->parentWidget()) { |