Bug 1421088 - Don't pass an nsIFrame* to DrawSegment. r?spohl draft
authorMarkus Stange <mstange@themasta.com>
Sat, 14 Apr 2018 22:26:24 -0400
changeset 782228 8c5038bffa6591bbfe29df86a67d876169b39b74
parent 782227 cfe60fa49b7db0371b61c9705b6f6a8b4778fc80
child 782229 da2bed0c8081bb0b6ef30043e5bea15f8e85ea9a
push id106503
push userbmo:mstange@themasta.com
push dateSun, 15 Apr 2018 02:35:01 +0000
reviewersspohl
bugs1421088
milestone61.0a1
Bug 1421088 - Don't pass an nsIFrame* to DrawSegment. r?spohl MozReview-Commit-ID: 7qe0V7agqzs
widget/cocoa/nsNativeThemeCocoa.h
widget/cocoa/nsNativeThemeCocoa.mm
--- a/widget/cocoa/nsNativeThemeCocoa.h
+++ b/widget/cocoa/nsNativeThemeCocoa.h
@@ -74,16 +74,21 @@ public:
     eDisclosureButtonOpen
   };
 
   enum class SpinButton : uint8_t {
     eUp,
     eDown
   };
 
+  enum class SegmentType : uint8_t {
+    eToolbarButton,
+    eTab
+  };
+
   struct ControlParams {
     ControlParams()
       : disabled(false)
       , insideActiveWindow(false)
       , pressed(false)
       , focused(false)
       , rtl(false)
     {}
@@ -135,16 +140,29 @@ public:
   };
 
   struct SpinButtonParams {
     mozilla::Maybe<SpinButton> pressedButton;
     bool disabled = false;
     bool insideActiveWindow = false;
   };
 
+  struct SegmentParams {
+    SegmentType segmentType = SegmentType::eToolbarButton;
+    bool insideActiveWindow = false;
+    bool pressed = false;
+    bool selected = false;
+    bool focused = false;
+    bool atLeftEnd = false;
+    bool atRightEnd = false;
+    bool drawsLeftSeparator = false;
+    bool drawsRightSeparator = false;
+    bool rtl = false;
+  };
+
   struct TreeHeaderCellParams {
     ControlParams controlParams;
     TreeSortDirection sortDirection = eTreeSortDirection_Natural;
     bool lastTreeHeaderCell = false;
   };
 
   nsNativeThemeCocoa();
 
@@ -201,41 +219,41 @@ public:
   static void DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect,
                                  CGFloat aUnifiedHeight, BOOL aIsMain, BOOL aIsFlipped);
 
 protected:
   virtual ~nsNativeThemeCocoa();
 
   nsIntMargin DirectionAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame);
   nsIFrame* SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter);
-  CGRect SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft,
-                               nsIFrame* aCurrent, nsIFrame* aRight);
   bool IsWindowSheet(nsIFrame* aFrame);
   ControlParams ComputeControlParams(nsIFrame* aFrame,
                                      mozilla::EventStates aEventState);
   MenuBackgroundParams ComputeMenuBackgroundParams(nsIFrame* aFrame,
                                                    mozilla::EventStates aEventState);
   MenuIconParams ComputeMenuIconParams(nsIFrame* aParams,
                                        mozilla::EventStates aEventState,
                                        MenuIcon aIcon);
   MenuItemParams ComputeMenuItemParams(nsIFrame* aFrame,
                                        mozilla::EventStates aEventState,
                                        bool aIsChecked);
+  SegmentParams ComputeSegmentParams(nsIFrame* aFrame,
+                                     mozilla::EventStates aEventState,
+                                     SegmentType aSegmentType);
   TreeHeaderCellParams ComputeTreeHeaderCellParams(nsIFrame* aFrame,
                                                    mozilla::EventStates aEventState);
 
   // HITheme drawing routines
   void DrawFrame(CGContextRef context, HIThemeFrameKind inKind,
                  const HIRect& inBoxRect, bool inReadOnly,
                  mozilla::EventStates inState);
   void DrawMeter(CGContextRef context, const HIRect& inBoxRect,
                  nsIFrame* aFrame);
   void DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
-                   mozilla::EventStates inState, nsIFrame* aFrame,
-                   const SegmentedControlRenderSettings& aSettings);
+                   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);
   void DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox,
                            const HIRect& inBoxRect,
                            const CheckboxOrRadioParams& aParams);
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2307,30 +2307,30 @@ nsNativeThemeCocoa::SeparatorResponsibil
     return nullptr;
   if (IsSelectedButton(aAfter))
     return aAfter;
   if (IsSelectedButton(aBefore) || IsPressedButton(aBefore))
     return aBefore;
   return aAfter;
 }
 
-CGRect
-nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft,
-                                          nsIFrame* aCurrent, nsIFrame* aRight)
+static CGRect
+SeparatorAdjustedRect(CGRect aRect, nsNativeThemeCocoa::SegmentParams aParams)
 {
   // A separator between two segments should always be located in the leftmost
   // pixel column of the segment to the right of the separator, regardless of
   // who ends up drawing it.
   // CoreUI draws the separators inside the drawing rect.
-  if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) {
-    // The left button draws the separator, so we need to make room for it.
+  if (!aParams.atLeftEnd && !aParams.drawsLeftSeparator) {
+    // The segment to the left of us draws the separator, so we need to make
+    // room for it.
     aRect.origin.x += 1;
     aRect.size.width -= 1;
   }
-  if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) {
+  if (aParams.drawsRightSeparator) {
     // We draw the right separator, so we need to extend the draw rect into the
     // segment to our right.
     aRect.size.width += 1;
   }
   return aRect;
 }
 
 static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast)
@@ -2343,66 +2343,96 @@ static NSString* ToolbarButtonPosition(B
   if (aIsLast)
     return @"kCUISegmentPositionLast";
   return @"kCUISegmentPositionMiddle";
 }
 
 struct SegmentedControlRenderSettings {
   const CGFloat* heights;
   const NSString* widgetName;
-  const BOOL ignoresPressedWhenSelected;
-  const BOOL isToolbarControl;
 };
 
 static const CGFloat tabHeights[3] = { 17, 20, 23 };
 
 static const SegmentedControlRenderSettings tabRenderSettings = {
-  tabHeights, @"tab", YES, NO
+  tabHeights, @"tab"
 };
 
 static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 };
 
 static const SegmentedControlRenderSettings toolbarButtonRenderSettings = {
-  toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES
+  toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve"
 };
 
+nsNativeThemeCocoa::SegmentParams
+nsNativeThemeCocoa::ComputeSegmentParams(nsIFrame* aFrame,
+                                         EventStates aEventState,
+                                         SegmentType aSegmentType)
+{
+  SegmentParams params;
+  params.segmentType = aSegmentType;
+  params.insideActiveWindow = FrameIsInActiveWindow(aFrame);
+  params.pressed = IsPressedButton(aFrame);
+  params.selected = IsSelectedButton(aFrame);
+  params.focused = aEventState.HasState(NS_EVENT_STATE_FOCUS);
+  bool isRTL = IsFrameRTL(aFrame);
+  nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL);
+  nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL);
+  params.atLeftEnd = !left;
+  params.atRightEnd = !right;
+  params.drawsLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame;
+  params.drawsRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame;
+  params.rtl = isRTL;
+  return params;
+}
+
+static SegmentedControlRenderSettings
+RenderSettingsForSegmentType(nsNativeThemeCocoa::SegmentType aSegmentType)
+{
+  switch (aSegmentType) {
+    case nsNativeThemeCocoa::SegmentType::eToolbarButton:
+      return toolbarButtonRenderSettings;
+    case nsNativeThemeCocoa::SegmentType::eTab:
+      return tabRenderSettings;
+  }
+}
+
 void
 nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
-                                EventStates inState, nsIFrame* aFrame,
-                                const SegmentedControlRenderSettings& aSettings)
+                                const SegmentParams& aParams)
 {
-  BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl);
-  BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS);
-  BOOL isSelected = IsSelectedButton(aFrame);
-  BOOL isPressed = IsPressedButton(aFrame);
-  if (isSelected && aSettings.ignoresPressedWhenSelected) {
-    isPressed = NO;
-  }
-
-  BOOL isRTL = IsFrameRTL(aFrame);
-  nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL);
-  nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL);
-  CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right);
-  BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame;
-  BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame;
-  NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f);
-
-  RenderWithCoreUI(drawRect, cgContext, [NSDictionary dictionaryWithObjectsAndKeys:
-            aSettings.widgetName, @"widget",
-            (isActive ? @"kCUIPresentationStateActiveKey" : @"kCUIPresentationStateInactive"), @"kCUIPresentationStateKey",
-            ToolbarButtonPosition(!left, !right), @"kCUIPositionKey",
-            [NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey",
-            [NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey",
-            [NSNumber numberWithBool:isSelected], @"value",
-            (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state",
-            [NSNumber numberWithBool:isFocused], @"focus",
-            CUIControlSizeForCocoaSize(controlSize), @"size",
-            [NSNumber numberWithBool:YES], @"is.flipped",
-            @"up", @"direction",
-            nil]);
+  SegmentedControlRenderSettings renderSettings =
+    RenderSettingsForSegmentType(aParams.segmentType);
+
+  NSControlSize controlSize =
+    FindControlSize(inBoxRect.size.height, renderSettings.heights, 4.0f);
+  CGRect drawRect = SeparatorAdjustedRect(inBoxRect, aParams);
+
+  NSDictionary* dict = @{
+    @"widget": renderSettings.widgetName,
+    @"kCUIPresentationStateKey":
+      (aParams.insideActiveWindow ? @"kCUIPresentationStateActiveKey"
+                                  : @"kCUIPresentationStateInactive"),
+    @"kCUIPositionKey":
+      ToolbarButtonPosition(aParams.atLeftEnd, aParams.atRightEnd),
+    @"kCUISegmentLeadingSeparatorKey":
+      [NSNumber numberWithBool:aParams.drawsLeftSeparator],
+    @"kCUISegmentTrailingSeparatorKey":
+      [NSNumber numberWithBool:aParams.drawsRightSeparator],
+    @"value": [NSNumber numberWithBool:aParams.selected],
+    @"state": (aParams.pressed ? @"pressed"
+                               : (aParams.insideActiveWindow ? @"normal"
+                                                             : @"inactive")),
+    @"focus": [NSNumber numberWithBool:aParams.focused],
+    @"size": CUIControlSizeForCocoaSize(controlSize),
+    @"is.flipped": [NSNumber numberWithBool:YES],
+    @"direction": @"up"
+  };
+
+  RenderWithCoreUI(drawRect, cgContext, dict);
 }
 
 nsIFrame*
 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
 {
   // Walk our parents to find a scrollbar frame
   nsIFrame* scrollbarFrame = aFrame;
   do {
@@ -2868,18 +2898,22 @@ nsNativeThemeCocoa::DrawWidgetBackground
           DrawSpinButton(cgContext, macRect, SpinButton::eUp, params);
         } else {
           DrawSpinButton(cgContext, macRect, SpinButton::eDown, params);
         }
       }
     }
       break;
 
-    case NS_THEME_TOOLBARBUTTON:
-      DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings);
+    case NS_THEME_TOOLBARBUTTON: {
+      SegmentParams params =
+        ComputeSegmentParams(aFrame, eventState, SegmentType::eToolbarButton);
+      params.insideActiveWindow = [NativeWindowForFrame(aFrame) isMainWindow];
+      DrawSegment(cgContext, macRect, params);
+    }
       break;
 
     case NS_THEME_SEPARATOR: {
       HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive };
       HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION);
     }
       break;
 
@@ -3241,18 +3275,22 @@ nsNativeThemeCocoa::DrawWidgetBackground
             @"kCUIVariantGradientSideBarSelection", @"kCUIVariantKey",
             (FrameIsInActiveWindow(aFrame) ? @"normal" : @"inactive"), @"state",
             @"gradient", @"widget",
             nil]);
       }
     }
       break;
 
-    case NS_THEME_TAB:
-      DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings);
+    case NS_THEME_TAB: {
+      SegmentParams params =
+        ComputeSegmentParams(aFrame, eventState, SegmentType::eTab);
+      params.pressed = params.pressed && !params.selected;
+      DrawSegment(cgContext, macRect, params);
+    }
       break;
 
     case NS_THEME_TABPANELS:
       DrawTabPanel(cgContext, macRect, aFrame);
       break;
 
     case NS_THEME_RESIZER:
       DrawResizer(cgContext, macRect, aFrame);