Bug 1471086 - Derive hover and active color for custom scrollbars. r=jmathies
authorXidorn Quan <me@upsuper.org>
Fri, 31 Aug 2018 21:33:41 +0000
changeset 482710 345436abd34e8079bea0323746d5ed1bd29e0e0d
parent 482709 21a1fadfbe5aa5f1e0f5004c071a5d1163e058ff
child 482711 33c7b0ea5caaa654c72cce43124db1ebc052dead
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersjmathies
bugs1471086
milestone63.0a1
Bug 1471086 - Derive hover and active color for custom scrollbars. r=jmathies Differential Revision: https://phabricator.services.mozilla.com/D4398
widget/windows/nsNativeThemeWin.cpp
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -4207,63 +4207,129 @@ nsNativeThemeWin::GetWidgetNativeDrawing
 
 static COLORREF
 ToColorRef(nscolor aColor)
 {
   return RGB(NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
 }
 
 static nscolor
-GetScrollbarArrowColor(nscolor aTrackColor)
+GetScrollbarButtonColor(nscolor aTrackColor, EventStates aStates)
+{
+  // See numbers in GetScrollbarArrowColor.
+  // This function is written based on ratios between values listed there.
+
+  bool isActive = aStates.HasState(NS_EVENT_STATE_ACTIVE);
+  bool isHover = aStates.HasState(NS_EVENT_STATE_HOVER);
+  if (!isActive && !isHover) {
+    return aTrackColor;
+  }
+  float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
+  if (isActive) {
+    if (luminance >= 0.18) {
+      luminance *= 0.134;
+    } else {
+      luminance /= 0.134;
+      luminance = std::min(luminance, 1.0f);
+    }
+  } else {
+    if (luminance >= 0.18) {
+      luminance *= 0.805;
+    } else {
+      luminance /= 0.805;
+    }
+  }
+  return RelativeLuminanceUtils::Adjust(aTrackColor, luminance);
+}
+
+static nscolor
+GetScrollbarArrowColor(nscolor aButtonColor)
 {
   // In Windows 10 scrollbar, there are several gray colors used:
   //
   // State  | Background (lum) | Arrow   | Contrast
   // -------+------------------+---------+---------
   // Normal | Gray 240 (87.1%) | Gray 96 |     5.5
   // Hover  | Gray 218 (70.1%) | Black   |    15.0
   // Active | Gray 96  (11.7%) | White   |     6.3
   //
   // Contrast value is computed based on the definition in
   // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
   //
   // This function is written based on these values.
 
-  float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
+  float luminance = RelativeLuminanceUtils::Compute(aButtonColor);
   // Color with luminance larger than 0.72 has contrast ratio over 4.6
   // to color with luminance of gray 96, so this value is chosen for
   // this range. It is the luminance of gray 221.
   if (luminance >= 0.72) {
     // ComputeRelativeLuminanceFromComponents(96). That function cannot
     // be constexpr because of std::pow.
     const float GRAY96_LUMINANCE = 0.117f;
-    return RelativeLuminanceUtils::Adjust(aTrackColor, GRAY96_LUMINANCE);
+    return RelativeLuminanceUtils::Adjust(aButtonColor, GRAY96_LUMINANCE);
   }
   // The contrast ratio of a color to black equals that to white when its
   // luminance is around 0.18, with a contrast ratio ~4.6 to both sides,
   // thus the value below. It's the lumanince of gray 118.
   if (luminance >= 0.18) {
     return NS_RGB(0, 0, 0);
   }
   return NS_RGB(255, 255, 255);
 }
 
+static nscolor
+AdjustScrollbarFaceColor(nscolor aFaceColor, EventStates aStates)
+{
+  // In Windows 10, scrollbar thumb has the following colors:
+  //
+  // State  | Color    | Luminance
+  // -------+----------+----------
+  // Normal | Gray 205 |     61.0%
+  // Hover  | Gray 166 |     38.1%
+  // Active | Gray 96  |     11.7%
+  //
+  // This function is written based on the ratios between the values.
+
+  bool isActive = aStates.HasState(NS_EVENT_STATE_ACTIVE);
+  bool isHover = aStates.HasState(NS_EVENT_STATE_HOVER);
+  if (!isActive && !isHover) {
+    return aFaceColor;
+  }
+  float luminance = RelativeLuminanceUtils::Compute(aFaceColor);
+  if (isActive) {
+    if (luminance >= 0.18) {
+      luminance *= 0.192;
+    } else {
+      luminance /= 0.192;
+    }
+  } else {
+    if (luminance >= 0.18) {
+      luminance *= 0.625;
+    } else {
+      luminance /= 0.625;
+    }
+  }
+  return RelativeLuminanceUtils::Adjust(aFaceColor, luminance);
+}
+
 // This tries to draw a Windows 10 style scrollbar with given colors.
 nsresult
 nsNativeThemeWin::DrawCustomScrollbarPart(gfxContext* aContext,
                                           nsIFrame* aFrame,
                                           ComputedStyle* aStyle,
                                           WidgetType aWidgetType,
                                           const nsRect& aRect,
                                           const nsRect& aClipRect)
 {
   MOZ_ASSERT(!aStyle->StyleUI()->mScrollbarFaceColor.IsAuto() ||
              !aStyle->StyleUI()->mScrollbarTrackColor.IsAuto() ||
              IsScrollbarWidthThin(aStyle));
 
+  EventStates eventStates = GetContentState(aFrame, aWidgetType);
+
   gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
           dr(aClipRect.X(), aClipRect.Y(),
              aClipRect.Width(), aClipRect.Height());
 
   nscolor trackColor =
     GetScrollbarTrackColor(aStyle, &GetScrollbarTrackColorForAuto);
   HBRUSH dcBrush = (HBRUSH) GetStockObject(DC_BRUSH);
 
@@ -4283,38 +4349,47 @@ nsNativeThemeWin::DrawCustomScrollbarPar
     }
 
     RECT widgetRect;
     nativeDrawing.TransformToNativeRect(tr, widgetRect);
     ::SetDCBrushColor(hdc, ToColorRef(trackColor));
     ::SelectObject(hdc, dcBrush);
     ::FillRect(hdc, &widgetRect, dcBrush);
 
+    RECT componentRect;
+    // Scrollbar thumb and button are two CSS pixels thinner than the track.
+    gfxRect tr2 = tr;
+    gfxFloat dev2css = round(AppUnitsPerCSSPixel() / p2a);
+    if (aWidgetType == StyleAppearance::ScrollbarthumbVertical ||
+        aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
+        aWidgetType == StyleAppearance::ScrollbarbuttonDown) {
+        tr2.Deflate(dev2css, 0);
+    } else {
+      tr2.Deflate(0, dev2css);
+    }
+    nativeDrawing.TransformToNativeRect(tr2, componentRect);
+
     switch (aWidgetType) {
       case StyleAppearance::ScrollbarthumbVertical:
       case StyleAppearance::ScrollbarthumbHorizontal: {
-        // Scrollbar thumb is two CSS pixels thinner than the track.
-        gfxRect tr2 = tr;
-        gfxFloat dev2css = round(AppUnitsPerCSSPixel() / p2a);
-        if (aWidgetType == StyleAppearance::ScrollbarthumbVertical) {
-          tr2.Deflate(dev2css, 0);
-        } else {
-          tr2.Deflate(0, dev2css);
-        }
-        nativeDrawing.TransformToNativeRect(tr2, widgetRect);
         nscolor faceColor =
           GetScrollbarFaceColor(aStyle, &GetScrollbarFaceColorForAuto);
+        faceColor = AdjustScrollbarFaceColor(faceColor, eventStates);
         ::SetDCBrushColor(hdc, ToColorRef(faceColor));
-        ::FillRect(hdc, &widgetRect, dcBrush);
+        ::FillRect(hdc, &componentRect, dcBrush);
         break;
       }
       case StyleAppearance::ScrollbarbuttonUp:
       case StyleAppearance::ScrollbarbuttonDown:
       case StyleAppearance::ScrollbarbuttonLeft:
       case StyleAppearance::ScrollbarbuttonRight: {
+        nscolor buttonColor = GetScrollbarButtonColor(trackColor, eventStates);
+        ::SetDCBrushColor(hdc, ToColorRef(buttonColor));
+        ::FillRect(hdc, &componentRect, dcBrush);
+
         // kPath is the path of scrollbar up arrow on Windows 10
         // in a 17x17 area.
         const LONG kSize = 17;
         const POINT kPath[] = {
           { 5, 9 }, { 8, 6 }, { 11, 9 }, { 11, 11 }, { 8, 8 }, { 5, 11 },
         };
         const size_t kCount = ArrayLength(kPath);
         // Calculate necessary parameters for positioning the arrow.
@@ -4339,17 +4414,17 @@ nsNativeThemeWin::DrawCustomScrollbarPar
           } else {
             path[i].x = kSize - kPath[i].y;
             path[i].y = kPath[i].x;
           }
           path[i].x = left + (LONG) round(unit * path[i].x);
           path[i].y = top + (LONG) round(unit * path[i].y);
         }
         // Paint the arrow.
-        COLORREF arrowColor = ToColorRef(GetScrollbarArrowColor(trackColor));
+        COLORREF arrowColor = ToColorRef(GetScrollbarArrowColor(buttonColor));
         // XXX Somehow we need to paint with both pen and brush to get
         //     the desired shape. Can we do so only with brush?
         ::SetDCPenColor(hdc, arrowColor);
         ::SetDCBrushColor(hdc, arrowColor);
         ::SelectObject(hdc, GetStockObject(DC_PEN));
         ::Polygon(hdc, path, kCount);
         break;
       }