Bug 1421088 - Don't pass an nsIFrame* to DrawScale, and create DrawScrollbarTrack and DrawScrollbarThumb. r?spohl draft
authorMarkus Stange <mstange@themasta.com>
Sat, 14 Apr 2018 23:06:29 -0400
changeset 782252 7e837f64d6870f413c0ad4efea2b1b354e645d8a
parent 782251 a1ecc026e5772f0e17d25c498a2cc979c3bcd942
child 782253 f98522ef73a7b1a1485e3fb43756475e3f5679ab
push id106506
push userbmo:mstange@themasta.com
push dateSun, 15 Apr 2018 03:32:46 +0000
reviewersspohl
bugs1421088
milestone61.0a1
Bug 1421088 - Don't pass an nsIFrame* to DrawScale, and create DrawScrollbarTrack and DrawScrollbarThumb. r?spohl MozReview-Commit-ID: 8MPG7ZlBvVD
widget/cocoa/nsNativeThemeCocoa.h
widget/cocoa/nsNativeThemeCocoa.mm
--- a/widget/cocoa/nsNativeThemeCocoa.h
+++ b/widget/cocoa/nsNativeThemeCocoa.h
@@ -203,16 +203,45 @@ public:
   };
 
   struct TreeHeaderCellParams {
     ControlParams controlParams;
     TreeSortDirection sortDirection = eTreeSortDirection_Natural;
     bool lastTreeHeaderCell = false;
   };
 
+  struct ScaleParams {
+    int32_t value = 0;
+    int32_t min = 0;
+    int32_t max = 0;
+    bool insideActiveWindow = false;
+    bool disabled = false;
+    bool focused = false;
+    bool horizontal = true;
+    bool reverse = false;
+  };
+
+  struct ScrollbarParams {
+    ScrollbarParams()
+      : overlay(false)
+      , rolledOver(false)
+      , small(false)
+      , horizontal(false)
+      , rtl(false)
+      , onDarkBackground(false)
+    {}
+
+    bool overlay : 1;
+    bool rolledOver : 1;
+    bool small : 1;
+    bool horizontal : 1;
+    bool rtl : 1;
+    bool onDarkBackground : 1;
+  };
+
   nsNativeThemeCocoa();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // The nsITheme interface.
   NS_IMETHOD DrawWidgetBackground(gfxContext* aContext,
                                   nsIFrame* aFrame,
                                   uint8_t aWidgetType,
@@ -284,29 +313,33 @@ protected:
   SearchFieldParams ComputeSearchFieldParams(nsIFrame* aFrame,
                                              mozilla::EventStates aEventState);
   ProgressParams ComputeProgressParams(nsIFrame* aFrame,
                                        mozilla::EventStates aEventState,
                                        bool aIsHorizontal);
   MeterParams ComputeMeterParams(nsIFrame* aFrame);
   TreeHeaderCellParams ComputeTreeHeaderCellParams(nsIFrame* aFrame,
                                                    mozilla::EventStates aEventState);
+  ScaleParams ComputeXULScaleParams(nsIFrame* aFrame,
+                                    mozilla::EventStates aEventState,
+                                    bool aIsHorizontal);
+  mozilla::Maybe<ScaleParams> ComputeHTMLScaleParams(nsIFrame* aFrame,
+                                                     mozilla::EventStates aEventState);
+  ScrollbarParams ComputeScrollbarParams(nsIFrame* aFrame, bool aIsHorizontal);
 
   // HITheme drawing routines
   void DrawTextBox(CGContextRef context, const HIRect& inBoxRect,
                    TextBoxParams aParams);
   void DrawMeter(CGContextRef context, const HIRect& inBoxRect,
                  const MeterParams& aParams);
   void DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
                    const SegmentParams& aParams);
   void DrawTabPanel(CGContextRef context, const HIRect& inBoxRect, nsIFrame* aFrame);
   void DrawScale(CGContextRef context, const HIRect& inBoxRect,
-                 mozilla::EventStates inState, bool inDirection,
-                 bool inIsReverse, int32_t inCurrentValue, int32_t inMinValue,
-                 int32_t inMaxValue, nsIFrame* aFrame);
+                 const ScaleParams& aParams);
   void DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox,
                            const HIRect& inBoxRect,
                            const CheckboxOrRadioParams& aParams);
   void DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect,
                        const SearchFieldParams& aParams);
   void DrawRoundedBezelPushButton(CGContextRef cgContext,
                                   const HIRect& inBoxRect,
                                   ControlParams aControlParams);
@@ -345,16 +378,20 @@ protected:
   void DrawSpinButton(CGContextRef context,
                       const HIRect& inBoxRect, SpinButton aDrawnButton,
                       const SpinButtonParams& aParams);
   void DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
                           const UnifiedToolbarParams& aParams);
   void DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect,
                      nsIFrame *aFrame);
   void DrawResizer(CGContextRef cgContext, const HIRect& aRect, nsIFrame *aFrame);
+  void DrawScrollbarThumb(CGContextRef cgContext, const CGRect& inBoxRect,
+                          ScrollbarParams aParams);
+  void DrawScrollbarTrack(CGContextRef cgContext, const CGRect& inBoxRect,
+                          ScrollbarParams aParams);
 
   // Scrollbars
   nsIFrame* GetParentScrollbarFrame(nsIFrame *aFrame);
   bool IsParentScrollbarRolledOver(nsIFrame* aFrame);
 
 private:
   NSButtonCell* mDisclosureButtonCell;
   NSButtonCell* mHelpButtonCell;
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2281,44 +2281,96 @@ nsNativeThemeCocoa::DrawTabPanel(CGConte
   tpdi.size = kHIThemeTabSizeNormal;
   tpdi.kind = kHIThemeTabKindNormal;
 
   HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+nsNativeThemeCocoa::ScaleParams
+nsNativeThemeCocoa::ComputeXULScaleParams(nsIFrame* aFrame,
+                                          EventStates aEventState,
+                                          bool aIsHorizontal)
+{
+  ScaleParams params;
+  params.value = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
+  params.min = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0);
+  params.max = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
+  if (!params.max) {
+    params.max = 100;
+  }
+
+  params.reverse =
+    aFrame->GetContent()->IsElement() &&
+    aFrame->GetContent()->AsElement()->AttrValueIs(
+      kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("reverse"),
+      eCaseMatters);
+  params.insideActiveWindow = FrameIsInActiveWindow(aFrame);
+  params.focused = aEventState.HasState(NS_EVENT_STATE_FOCUS);
+  params.disabled = IsDisabled(aFrame, aEventState);
+  params.horizontal = aIsHorizontal;
+  return params;
+}
+
+Maybe<nsNativeThemeCocoa::ScaleParams>
+nsNativeThemeCocoa::ComputeHTMLScaleParams(nsIFrame* aFrame,
+                                           EventStates aEventState)
+{
+  nsRangeFrame *rangeFrame = do_QueryFrame(aFrame);
+  if (!rangeFrame) {
+    return Nothing();
+  }
+
+  bool isHorizontal = IsRangeHorizontal(aFrame);
+
+  // ScaleParams requires integer min, max and value. This is purely for
+  // drawing, so we normalize to a range 0-1000 here.
+  ScaleParams params;
+  params.value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000);
+  params.min = 0;
+  params.max = 1000;
+  params.reverse = !isHorizontal || rangeFrame->IsRightToLeft();
+  params.insideActiveWindow = FrameIsInActiveWindow(aFrame);
+  params.focused = aEventState.HasState(NS_EVENT_STATE_FOCUS);
+  params.disabled = IsDisabled(aFrame, aEventState);
+  params.horizontal = isHorizontal;
+  return Some(params);
+}
+
 void
 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
-                              EventStates inState, bool inIsVertical,
-                              bool inIsReverse, int32_t inCurrentValue,
-                              int32_t inMinValue, int32_t inMaxValue,
-                              nsIFrame* aFrame)
+                              const ScaleParams& aParams)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   HIThemeTrackDrawInfo tdi;
 
   tdi.version = 0;
   tdi.kind = kThemeMediumSlider;
   tdi.bounds = inBoxRect;
-  tdi.min = inMinValue;
-  tdi.max = inMaxValue;
-  tdi.value = inCurrentValue;
+  tdi.min = aParams.min;
+  tdi.max = aParams.max;
+  tdi.value = aParams.value;
   tdi.attributes = kThemeTrackShowThumb;
-  if (!inIsVertical)
+  if (aParams.horizontal) {
     tdi.attributes |= kThemeTrackHorizontal;
-  if (inIsReverse)
+  }
+  if (aParams.reverse) {
     tdi.attributes |= kThemeTrackRightToLeft;
-  if (inState.HasState(NS_EVENT_STATE_FOCUS))
+  }
+  if (aParams.focused) {
     tdi.attributes |= kThemeTrackHasFocus;
-  if (IsDisabled(aFrame, inState))
+  }
+  if (aParams.disabled) {
     tdi.enableState = kThemeTrackDisabled;
-  else
-    tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
+  } else {
+    tdi.enableState =
+      aParams.insideActiveWindow ? kThemeTrackActive : kThemeTrackInactive;
+  }
   tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
   tdi.trackInfo.slider.pressState = 0;
 
   HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
@@ -2593,16 +2645,91 @@ nsNativeThemeCocoa::DrawResizer(CGContex
   drawInfo.size = kHIThemeGrowBoxSizeNormal;
 
   RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
                                   IsFrameRTL(aFrame));
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+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 = aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
+  params.horizontal = aIsHorizontal;
+  params.onDarkBackground = IsDarkBackground(aFrame);
+  return params;
+}
+
+void
+nsNativeThemeCocoa::DrawScrollbarThumb(CGContextRef cgContext,
+                                       const CGRect& inBoxRect,
+                                       ScrollbarParams aParams)
+{
+  CGRect drawRect = inBoxRect;
+  if (aParams.overlay && !aParams.rolledOver) {
+    if (aParams.horizontal) {
+      drawRect.origin.y += 4;
+      drawRect.size.height -= 4;
+    } else {
+      if (!aParams.rtl) {
+        drawRect.origin.x += 4;
+      }
+      drawRect.size.width -= 4;
+    }
+  }
+  NSDictionary* options = @{
+    @"widget": (aParams.overlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"),
+    @"size": (aParams.small ? @"small" : @"regular"),
+    @"kCUIOrientationKey":
+      (aParams.horizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"),
+    @"kCUIVariantKey":
+      (aParams.overlay && aParams.onDarkBackground ? @"kCUIVariantWhite" : @""),
+    @"indiconly": [NSNumber numberWithBool:YES],
+    @"kCUIThumbProportionKey": [NSNumber numberWithBool:YES],
+    @"is.flipped": [NSNumber numberWithBool:YES],
+  };
+  if (aParams.rolledOver) {
+    NSMutableDictionary* mutableOptions = [options mutableCopy];
+    [mutableOptions setObject:@"rollover" forKey:@"state"];
+    options = mutableOptions;
+  }
+  RenderWithCoreUI(drawRect, cgContext, options, true);
+}
+
+void
+nsNativeThemeCocoa::DrawScrollbarTrack(CGContextRef cgContext,
+                                       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);
+}
+
 static void
 DrawVibrancyBackground(CGContextRef cgContext, CGRect inBoxRect,
                        nsIFrame* aFrame, nsITheme::ThemeGeometryType aThemeGeometryType)
 {
   DrawVibrancyBackground(cgContext, inBoxRect,
                          VibrancyFillColor(aFrame, aThemeGeometryType), 0);
 }
 
@@ -3086,93 +3213,43 @@ nsNativeThemeCocoa::DrawWidgetBackground
       // do nothing, taken care of by individual header cells
     case NS_THEME_TREEHEADERSORTARROW:
       // do nothing, taken care of by treeview header
     case NS_THEME_TREELINE:
       // do nothing, these lines don't exist on macos
       break;
 
     case NS_THEME_SCALE_HORIZONTAL:
-    case NS_THEME_SCALE_VERTICAL: {
-      int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
-      int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0);
-      int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
-      if (!maxpos)
-        maxpos = 100;
-
-      bool reverse =
-        aFrame->GetContent()->IsElement() &&
-        aFrame->GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
-                                                       NS_LITERAL_STRING("reverse"), eCaseMatters);
-      DrawScale(cgContext, macRect, eventState,
-                (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse,
-                curpos, minpos, maxpos, aFrame);
-    }
+    case NS_THEME_SCALE_VERTICAL:
+      DrawScale(cgContext, macRect,
+                ComputeXULScaleParams(aFrame, eventState,
+                                      aWidgetType == NS_THEME_SCALE_HORIZONTAL));
       break;
 
     case NS_THEME_SCALETHUMB_HORIZONTAL:
     case NS_THEME_SCALETHUMB_VERTICAL:
       // do nothing, drawn by scale
       break;
 
     case NS_THEME_RANGE: {
-      nsRangeFrame *rangeFrame = do_QueryFrame(aFrame);
-      if (!rangeFrame) {
-        break;
+      Maybe<ScaleParams> params = ComputeHTMLScaleParams(aFrame, eventState);
+      if (params) {
+        DrawScale(cgContext, macRect, *params);
       }
-      // DrawScale requires integer min, max and value. This is purely for
-      // drawing, so we normalize to a range 0-1000 here.
-      int32_t value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000);
-      int32_t min = 0;
-      int32_t max = 1000;
-      bool isVertical = !IsRangeHorizontal(aFrame);
-      bool reverseDir = isVertical || rangeFrame->IsRightToLeft();
-      DrawScale(cgContext, macRect, eventState, isVertical, reverseDir,
-                value, min, max, aFrame);
       break;
     }
 
     case NS_THEME_SCROLLBAR_SMALL:
     case NS_THEME_SCROLLBAR:
       break;
     case NS_THEME_SCROLLBARTHUMB_VERTICAL:
-    case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: {
-      BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars();
-      BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL);
-      BOOL isRolledOver = IsParentScrollbarRolledOver(aFrame);
-      nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
-      bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
-      if (isOverlay && !isRolledOver) {
-        if (isHorizontal) {
-          macRect.origin.y += 4;
-          macRect.size.height -= 4;
-        } else {
-          if (aFrame->StyleVisibility()->mDirection !=
-              NS_STYLE_DIRECTION_RTL) {
-            macRect.origin.x += 4;
-          }
-          macRect.size.width -= 4;
-        }
-      }
-      const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
-      NSMutableDictionary* options = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-        (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
-        (isSmall ? @"small" : @"regular"), @"size",
-        (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
-        (isOverlay && isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""),
-          @"kCUIVariantKey",
-        [NSNumber numberWithBool:YES], @"indiconly",
-        [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
-        [NSNumber numberWithBool:YES], @"is.flipped",
-        nil];
-      if (isRolledOver) {
-        [options setObject:@"rollover" forKey:@"state"];
-      }
-      RenderWithCoreUI(macRect, cgContext, options, true);
-    }
+    case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+      DrawScrollbarThumb(cgContext, macRect,
+        ComputeScrollbarParams(
+          aFrame, aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL));
       break;
 
     case NS_THEME_SCROLLBARBUTTON_UP:
     case NS_THEME_SCROLLBARBUTTON_LEFT:
 #if SCROLLBARS_VISUAL_DEBUG
       CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6);
       CGContextFillRect(cgContext, macRect);
 #endif
@@ -3180,36 +3257,20 @@ nsNativeThemeCocoa::DrawWidgetBackground
     case NS_THEME_SCROLLBARBUTTON_DOWN:
     case NS_THEME_SCROLLBARBUTTON_RIGHT:
 #if SCROLLBARS_VISUAL_DEBUG
       CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6);
       CGContextFillRect(cgContext, macRect);
 #endif
     break;
     case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
-    case NS_THEME_SCROLLBARTRACK_VERTICAL: {
-      BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars();
-      if (!isOverlay || IsParentScrollbarRolledOver(aFrame)) {
-        BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL);
-        nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
-        bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
-        const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
-        RenderWithCoreUI(macRect, cgContext,
-                [NSDictionary dictionaryWithObjectsAndKeys:
-                  (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
-                  (isSmall ? @"small" : @"regular"), @"size",
-                  (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
-                  (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
-                  [NSNumber numberWithBool:YES], @"noindicator",
-                  [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
-                  [NSNumber numberWithBool:YES], @"is.flipped",
-                  nil],
-                true);
-      }
-    }
+    case NS_THEME_SCROLLBARTRACK_VERTICAL:
+      DrawScrollbarTrack(cgContext, macRect,
+        ComputeScrollbarParams(
+          aFrame, aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL));
       break;
 
     case NS_THEME_TEXTFIELD_MULTILINE: {
       // we have to draw this by hand because there is no HITheme value for it
       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
 
       CGContextFillRect(cgContext, macRect);