--- a/widget/cocoa/nsNativeThemeCocoa.h
+++ b/widget/cocoa/nsNativeThemeCocoa.h
@@ -227,24 +227,29 @@ public:
struct ScrollbarParams {
ScrollbarParams()
: overlay(false)
, rolledOver(false)
, small(false)
, horizontal(false)
, rtl(false)
, onDarkBackground(false)
+ , custom(false)
{}
bool overlay : 1;
bool rolledOver : 1;
bool small : 1;
bool horizontal : 1;
bool rtl : 1;
bool onDarkBackground : 1;
+ bool custom : 1;
+ // Two colors only used when custom is true.
+ nscolor trackColor = NS_RGBA(0, 0, 0, 0);
+ nscolor faceColor = NS_RGBA(0, 0, 0, 0);
};
enum Widget : uint8_t {
eColorFill, // mozilla::gfx::Color
eSheetBackground,
eDialogBackground,
eMenuBackground, // MenuBackgroundParams
eMenuIcon, // MenuIconParams
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -26,16 +26,17 @@
#include "nsPresContext.h"
#include "nsGkAtoms.h"
#include "nsCocoaFeatures.h"
#include "nsCocoaWindow.h"
#include "nsNativeThemeColors.h"
#include "nsIScrollableFrame.h"
#include "mozilla/EventStates.h"
#include "mozilla/Range.h"
+#include "mozilla/RelativeLuminanceUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLMeterElement.h"
#include "mozilla/layers/StackingContextHelper.h"
#include "nsLookAndFeel.h"
#include "VibrancyManager.h"
#include "gfxContext.h"
#include "gfxQuartzSurface.h"
@@ -1356,16 +1357,36 @@ nsNativeThemeCocoa::ComputeMenuItemParam
}
static void
SetCGContextFillColor(CGContextRef cgContext, const Color& aColor)
{
CGContextSetRGBFillColor(cgContext, aColor.r, aColor.g, aColor.b, aColor.a);
}
+static void
+SetCGContextFillColor(CGContextRef cgContext, nscolor aColor)
+{
+ CGContextSetRGBFillColor(cgContext,
+ NS_GET_R(aColor) / 255.0f,
+ NS_GET_G(aColor) / 255.0f,
+ NS_GET_B(aColor) / 255.0f,
+ NS_GET_A(aColor) / 255.0f);
+}
+
+static void
+SetCGContextStrokeColor(CGContextRef cgContext, nscolor aColor)
+{
+ CGContextSetRGBStrokeColor(cgContext,
+ NS_GET_R(aColor) / 255.0f,
+ NS_GET_G(aColor) / 255.0f,
+ NS_GET_B(aColor) / 255.0f,
+ NS_GET_A(aColor) / 255.0f);
+}
+
void
nsNativeThemeCocoa::DrawMenuItem(CGContextRef cgContext,
const CGRect& inBoxRect,
const MenuItemParams& aParams)
{
if (aParams.vibrancyColor) {
SetCGContextFillColor(cgContext, *aParams.vibrancyColor);
CGContextFillRect(cgContext, inBoxRect);
@@ -2689,38 +2710,95 @@ nsNativeThemeCocoa::DrawResizer(CGContex
drawInfo.size = kHIThemeGrowBoxSizeNormal;
RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
aIsRTL);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
+static nscolor
+GetAutoScrollbarTrackColor(ComputedStyle* aStyle)
+{
+ // Use the default scrollbar color. XXX Can we get it from the system?
+ return NS_RGB(0xFA, 0xFA, 0xFA);
+}
+
+static nscolor
+GetAutoScrollbarFaceColor(ComputedStyle* aStyle)
+{
+ // Use the default scrollbar color. We may want to derive from track
+ // color at some point.
+ return NS_RGB(0xC1, 0xC1, 0xC1);
+}
+
nsNativeThemeCocoa::ScrollbarParams
nsNativeThemeCocoa::ComputeScrollbarParams(nsIFrame* aFrame, bool aIsHorizontal)
{
ScrollbarParams params;
params.overlay = nsLookAndFeel::UseOverlayScrollbars();
params.rolledOver = IsParentScrollbarRolledOver(aFrame);
nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
params.small =
(scrollbarFrame &&
scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
params.rtl = IsFrameRTL(aFrame);
params.horizontal = aIsHorizontal;
params.onDarkBackground = IsDarkBackground(aFrame);
+ // Don't use custom scrollbars for overlay scrollbars since they are
+ // generally good enough for use cases of custom scrollbars.
+ if (!params.overlay &&
+ aFrame->StyleUserInterface()->HasCustomScrollbars()) {
+ ComputedStyle* cs = aFrame->Style();
+ params.custom = true;
+ params.trackColor = GetScrollbarTrackColor(cs, &GetAutoScrollbarTrackColor);
+ params.faceColor = GetScrollbarFaceColor(cs, &GetAutoScrollbarFaceColor);
+ }
return params;
}
void
nsNativeThemeCocoa::DrawScrollbarThumb(CGContextRef cgContext,
const CGRect& inBoxRect,
ScrollbarParams aParams)
{
CGRect drawRect = inBoxRect;
+ if (aParams.custom) {
+ const CGFloat kWidthRatio = 8.0f / 15.0f;
+ const CGFloat kLengthReductionRatio = 2.0f / 15.0f;
+ const CGFloat kOrthogonalDirOffset = 4.0f / 15.0f;
+ const CGFloat kParallelDirOffset = 1.0f / 15.0f;
+ CGFloat baseSize, cornerWidth;
+ CGRect thumbRect = inBoxRect;
+ if (aParams.horizontal) {
+ baseSize = inBoxRect.size.height;
+ thumbRect.size.height *= kWidthRatio;
+ thumbRect.size.width -= baseSize * kLengthReductionRatio;
+ thumbRect.origin.y += baseSize * kOrthogonalDirOffset;
+ thumbRect.origin.x += baseSize * kParallelDirOffset;
+ cornerWidth = thumbRect.size.height / 2.0f;
+ } else {
+ baseSize = inBoxRect.size.width;
+ thumbRect.size.width *= kWidthRatio;
+ thumbRect.size.height -= baseSize * kLengthReductionRatio;
+ thumbRect.origin.x += baseSize * kOrthogonalDirOffset;
+ thumbRect.origin.y += baseSize * kParallelDirOffset;
+ cornerWidth = thumbRect.size.width / 2.0f;
+ }
+ CGPathRef path = CGPathCreateWithRoundedRect(thumbRect,
+ cornerWidth,
+ cornerWidth,
+ nullptr);
+ CGContextAddPath(cgContext, path);
+ CGPathRelease(path);
+ SetCGContextFillColor(cgContext, aParams.faceColor);
+ CGContextFillPath(cgContext);
+ return;
+ }
+
if (aParams.overlay && !aParams.rolledOver) {
if (aParams.horizontal) {
drawRect.origin.y += 4;
drawRect.size.height -= 4;
} else {
if (!aParams.rtl) {
drawRect.origin.x += 4;
}
@@ -2751,27 +2829,89 @@ nsNativeThemeCocoa::DrawScrollbarTrack(C
const CGRect& inBoxRect,
ScrollbarParams aParams)
{
if (aParams.overlay && !aParams.rolledOver) {
// Non-hovered overlay scrollbars don't have a track. Draw nothing.
return;
}
- RenderWithCoreUI(inBoxRect, cgContext,
- [NSDictionary dictionaryWithObjectsAndKeys:
- (aParams.overlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
- (aParams.small ? @"small" : @"regular"), @"size",
- (aParams.horizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
- (aParams.onDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
- [NSNumber numberWithBool:YES], @"noindicator",
- [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
- [NSNumber numberWithBool:YES], @"is.flipped",
- nil],
- true);
+ if (!aParams.custom) {
+ RenderWithCoreUI(inBoxRect, cgContext,
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ (aParams.overlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
+ (aParams.small ? @"small" : @"regular"), @"size",
+ (aParams.horizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
+ (aParams.onDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
+ [NSNumber numberWithBool:YES], @"noindicator",
+ [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
+ [NSNumber numberWithBool:YES], @"is.flipped",
+ nil],
+ true);
+ return;
+ }
+
+ nscolor color = aParams.trackColor;
+ // Paint the background color
+ SetCGContextFillColor(cgContext, color);
+ CGContextFillRect(cgContext, inBoxRect);
+ // Paint decorations
+ float luminance = RelativeLuminanceUtils::Compute(color);
+ nscolor innerColor, shadowColor, outerColor;
+ if (luminance >= 0.5) {
+ innerColor = RelativeLuminanceUtils::Adjust(color, luminance * 0.836);
+ shadowColor = RelativeLuminanceUtils::Adjust(color, luminance * 0.982);
+ outerColor = RelativeLuminanceUtils::Adjust(color, luminance * 0.886);
+ } else {
+ innerColor = RelativeLuminanceUtils::Adjust(color, luminance * 1.196);
+ shadowColor = RelativeLuminanceUtils::Adjust(color, luminance * 1.018);
+ outerColor = RelativeLuminanceUtils::Adjust(color, luminance * 1.129);
+ }
+ CGPoint innerPoints[2];
+ CGPoint shadowPoints[2];
+ CGPoint outerPoints[2];
+ if (aParams.horizontal) {
+ innerPoints[0].x = inBoxRect.origin.x;
+ innerPoints[0].y = inBoxRect.origin.y + 0.5f;
+ innerPoints[1].x = innerPoints[0].x + inBoxRect.size.width;
+ innerPoints[1].y = innerPoints[0].y;
+ shadowPoints[0].x = innerPoints[0].x;
+ shadowPoints[0].y = innerPoints[0].y + 1.0f;
+ shadowPoints[1].x = innerPoints[1].x;
+ shadowPoints[1].y = shadowPoints[0].y;
+ outerPoints[0].x = innerPoints[0].x;
+ outerPoints[0].y = innerPoints[0].y + inBoxRect.size.height - 1;
+ outerPoints[1].x = innerPoints[1].x;
+ outerPoints[1].y = outerPoints[0].y;
+ } else {
+ if (aParams.rtl) {
+ innerPoints[0].x = inBoxRect.origin.x + inBoxRect.size.width - 0.5f;
+ shadowPoints[0].x = innerPoints[0].x - 1.0f;
+ outerPoints[0].x = inBoxRect.origin.x + 0.5f;
+ } else {
+ innerPoints[0].x = inBoxRect.origin.x + 0.5f;
+ shadowPoints[0].x = innerPoints[0].x + 1.0f;
+ outerPoints[0].x = inBoxRect.origin.x + inBoxRect.size.width - 0.5f;
+ }
+ innerPoints[0].y = inBoxRect.origin.y;
+ innerPoints[1].x = innerPoints[0].x;
+ innerPoints[1].y = innerPoints[0].y + inBoxRect.size.height;
+ shadowPoints[0].y = innerPoints[0].y;
+ shadowPoints[1].x = shadowPoints[0].x;
+ shadowPoints[1].y = innerPoints[1].y;
+ outerPoints[0].y = innerPoints[0].y;
+ outerPoints[1].x = outerPoints[0].x;
+ outerPoints[1].y = innerPoints[1].y;
+ }
+ SetCGContextStrokeColor(cgContext, innerColor);
+ CGContextStrokeLineSegments(cgContext, innerPoints, 2);
+ SetCGContextStrokeColor(cgContext, shadowColor);
+ CGContextStrokeLineSegments(cgContext, shadowPoints, 2);
+ SetCGContextStrokeColor(cgContext, outerColor);
+ CGContextStrokeLineSegments(cgContext, outerPoints, 2);
}
static const Color kTooltipBackgroundColor(0.996, 1.000, 0.792, 0.950);
static const Color kMultilineTextFieldTopBorderColor(0.4510, 0.4510, 0.4510, 1.0);
static const Color kMultilineTextFieldSidesAndBottomBorderColor(0.6, 0.6, 0.6, 1.0);
static const Color kListboxTopBorderColor(0.557, 0.557, 0.557, 1.0);
static const Color kListBoxSidesAndBottomBorderColor(0.745, 0.745, 0.745, 1.0);