diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 6990cb6fe8..2b2f729972 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -1661,10 +1661,34 @@ void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOpti tdi->kind = kThemeSmallSlider; break; } + + bool usePlainKnob = slider->tickPosition == QSlider::NoTicks + || slider->tickPosition == QSlider::TicksBothSides; + tdi->bounds = qt_hirectForQRect(slider->rect); - tdi->min = slider->minimum; - tdi->max = slider->maximum; - tdi->value = slider->sliderPosition; + if (isScrollbar || QSysInfo::MacintoshVersion <= QSysInfo::MV_10_9) { + tdi->min = slider->minimum; + tdi->max = slider->maximum; + tdi->value = slider->sliderPosition; + } else { + // 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(); + + if (usePlainKnob || slider->orientation == Qt::Horizontal) { + int endsCorrection = usePlainKnob ? 25 : 10; + tdi->value = (tdi->max + 2 * endsCorrection) * (slider->sliderPosition - slider->minimum) + / (slider->maximum - slider->minimum) - endsCorrection; + } else { + tdi->value = (tdi->max + 30) * (slider->sliderPosition - slider->minimum) + / (slider->maximum - slider->minimum) - 20; + } + } tdi->attributes = kThemeTrackShowThumb; if (slider->upsideDown) tdi->attributes |= kThemeTrackRightToLeft; @@ -1681,7 +1705,7 @@ void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOpti // Tiger broke reverse scroll bars so put them back and "fake it" if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) { tdi->attributes &= ~kThemeTrackRightToLeft; - tdi->value = tdi->max - slider->sliderPosition; + tdi->value = tdi->max - tdi->value; } tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive @@ -1689,7 +1713,7 @@ void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOpti if (!isScrollbar) { if (slider->state & QStyle::QStyle::State_HasFocus) tdi->attributes |= kThemeTrackHasFocus; - if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides) + if (usePlainKnob) tdi->trackInfo.slider.thumbDir = kThemeThumbPlain; else if (slider->tickPosition == QSlider::TicksAbove) tdi->trackInfo.slider.thumbDir = kThemeThumbUpward; @@ -1815,6 +1839,12 @@ NSView *QMacStylePrivate::cocoaControl(QCocoaWidget widget, QPoint *offset) cons bv = [[NSPopUpButton alloc] init]; else if (widget.first == QCocoaComboBox) bv = [[NSComboBox alloc] init]; + else if (widget.first == QCocoaHorizontalSlider) + bv = [[NSSlider alloc] init]; + 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)]; else bv = [[NSButton alloc] init]; @@ -5315,6 +5345,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex // because on Tiger I only "fake" the reverse stuff. bool reverseHorizontal = (slider->direction == Qt::RightToLeft && slider->orientation == Qt::Horizontal); + if ((reverseHorizontal && slider->activeSubControls == SC_ScrollBarAddLine) || (!reverseHorizontal @@ -5365,6 +5396,9 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex tdi.attributes |= kThemeTrackHideTrack; } + const bool usingYosemiteOrLater = QSysInfo::MacintoshVersion > QSysInfo::MV_10_9; + const bool isHorizontal = slider->orientation == Qt::Horizontal; + #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (cc == CC_ScrollBar && proxy()->styleHint(SH_ScrollBar_Transient, opt, widget)) { bool wasActive = false; @@ -5449,8 +5483,6 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } } - const bool isHorizontal = slider->orientation == Qt::Horizontal; - CGContextSaveGState(cg); [NSGraphicsContext saveGraphicsState]; @@ -5535,9 +5567,86 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex { d->stopAnimation(opt->styleObject); - HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg, - kHIThemeOrientationNormal); + if (usingYosemiteOrLater && cc == CC_Slider) { + // 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. + HIRect 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; + } + + // Yosemite demands its blue progress track when no tickmarks are present + if (!(slider->subControls & SC_SliderTickmarks)) { + QCocoaWidgetKind sliderKind = slider->orientation == Qt::Horizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider; + NSSlider *sl = (NSSlider *)d->cocoaControl(QCocoaWidget(sliderKind, QAquaSizeLarge), 0); + sl.minValue = slider->minimum; + sl.maxValue = slider->maximum; + sl.intValue = slider->sliderValue; + sl.enabled = slider->state & QStyle::State_Enabled; + d->drawNSViewInRect(sl, opt->rect, p, ^(NSRect rect, CGContextRef ctx) { + if (slider->upsideDown) { + if (isHorizontal) { + CGContextTranslateCTM(ctx, rect.size.width, 0); + CGContextScaleCTM(ctx, -1, 1); + } + } else if (!isHorizontal) { + CGContextTranslateCTM(ctx, 0, rect.size.height); + CGContextScaleCTM(ctx, 1, -1); + } + [sl.cell drawBarInside:tdi.bounds flipped:NO]; + // No need to restore the CTM later, the context has been saved + // and will be restored at the end of drawNSViewInRect() + }); + tdi.attributes |= kThemeTrackHideTrack; + } else { + tdi.attributes &= ~(kThemeTrackShowThumb | kThemeTrackHasFocus); + HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg, + kHIThemeOrientationNormal); + tdi.attributes |= kThemeTrackHideTrack | kThemeTrackShowThumb; + } + + tdi.bounds = bounds; + } + if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) { + + HIRect bounds; + if (usingYosemiteOrLater) { + // 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 (qt_mac_is_metal(widget)) { if (tdi.enableState == kThemeTrackInactive) tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like @@ -5559,16 +5668,37 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex cg, kHIThemeOrientationNormal); tdi.trackInfo.slider.thumbDir = kThemeThumbUpward; + if (usingYosemiteOrLater) { + 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 (usingYosemiteOrLater) + tdi.bounds = bounds; + } + + if (usingYosemiteOrLater && cc == CC_Slider) { + // Still as part of fixing the min and max positions, + // we also adjust the knob position. We can do this + // because it's rendered separately from the track. + if (slider->orientation == Qt::Vertical) { + if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward) // pointing right + tdi.bounds.origin.x -= 2; } } + + HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg, + kHIThemeOrientationNormal); } } break; diff --git a/src/widgets/styles/qmacstyle_mac_p_p.h b/src/widgets/styles/qmacstyle_mac_p_p.h index 1c5e7b5c45..f36ba2c190 100644 --- a/src/widgets/styles/qmacstyle_mac_p_p.h +++ b/src/widgets/styles/qmacstyle_mac_p_p.h @@ -127,7 +127,9 @@ enum QCocoaWidgetKind { QCocoaComboBox, // Editable QComboBox QCocoaPopupButton, // Non-editable QComboBox QCocoaPushButton, - QCocoaRadioButton + QCocoaRadioButton, + QCocoaHorizontalSlider, + QCocoaVerticalSlider }; typedef QPair QCocoaWidget;