Bug 1045213 - Use vibrancy effect for context menus. r=smichaud
authorMarkus Stange <mstange@themasta.com>
Wed, 04 Feb 2015 17:25:18 -0500
changeset 227603 984a21c9423cf512dc74b941f02cb71f225ea304
parent 227602 657fc2d2da4a3352ee8c5c05e662d1cae2445993
child 227604 4e3291b79e574b4f2b922455143b9287657b01dc
push id28235
push usercbook@mozilla.com
push dateThu, 05 Feb 2015 13:47:35 +0000
treeherdermozilla-central@58ce6051edf5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmichaud
bugs1045213
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1045213 - Use vibrancy effect for context menus. r=smichaud
layout/base/nsDisplayList.cpp
widget/cocoa/VibrancyManager.h
widget/cocoa/VibrancyManager.mm
widget/cocoa/nsChildView.mm
widget/cocoa/nsNativeThemeCocoa.mm
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2791,16 +2791,19 @@ nsDisplayThemedBackground::nsDisplayThem
   mAppearance = disp->mAppearance;
   mFrame->IsThemed(disp, &mThemeTransparency);
 
   // Perform necessary RegisterThemeGeometry
   switch (disp->mAppearance) {
     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
     case NS_THEME_TOOLBAR:
     case NS_THEME_TOOLTIP:
+    case NS_THEME_MENUPOPUP:
+    case NS_THEME_MENUITEM:
+    case NS_THEME_CHECKMENUITEM:
     case NS_THEME_WINDOW_TITLEBAR:
     case NS_THEME_WINDOW_BUTTON_BOX:
     case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
     case NS_THEME_MAC_VIBRANCY_LIGHT:
     case NS_THEME_MAC_VIBRANCY_DARK:
       RegisterThemeGeometry(aBuilder, aFrame);
       break;
--- a/widget/cocoa/VibrancyManager.h
+++ b/widget/cocoa/VibrancyManager.h
@@ -19,17 +19,18 @@
 class nsChildView;
 class nsIntRegion;
 
 namespace mozilla {
 
 enum class VibrancyType {
   LIGHT,
   DARK,
-  TOOLTIP
+  TOOLTIP,
+  MENU
 };
 
 /**
  * VibrancyManager takes care of updating the vibrant regions of a window.
  * Vibrancy is a visual look that was introduced on OS X starting with 10.10.
  * An app declares vibrant window regions to the window server, and the window
  * server will display a blurred rendering of the screen contents from behind
  * the window in these areas, behind the actual window contents. Consequently,
--- a/widget/cocoa/VibrancyManager.mm
+++ b/widget/cocoa/VibrancyManager.mm
@@ -157,16 +157,17 @@ CreateEffectViewClass()
 
 static id
 AppearanceForVibrancyType(VibrancyType aType)
 {
   Class NSAppearanceClass = NSClassFromString(@"NSAppearance");
   switch (aType) {
     case VibrancyType::LIGHT:
     case VibrancyType::TOOLTIP:
+    case VibrancyType::MENU:
       return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
                                      withObject:@"NSAppearanceNameVibrantLight"];
     case VibrancyType::DARK:
       return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
                                      withObject:@"NSAppearanceNameVibrantDark"];
   }
 }
 
@@ -184,19 +185,19 @@ enum {
 
 NSView*
 VibrancyManager::CreateEffectView(VibrancyType aType, NSRect aRect)
 {
   static Class EffectViewClass = CreateEffectViewClass();
   NSView* effectView = [[EffectViewClass alloc] initWithFrame:aRect];
   [effectView performSelector:@selector(setAppearance:)
                    withObject:AppearanceForVibrancyType(aType)];
-  if (aType == VibrancyType::TOOLTIP) {
-    // Tooltip windows never become active, so we need to tell the vibrancy
-    // effect to look active regardless of window state.
+  if (aType == VibrancyType::TOOLTIP || aType == VibrancyType::MENU) {
+    // Tooltip and menu windows never become active, so we need to tell the
+    // vibrancy effect to look active regardless of window state.
     [effectView setState:NSVisualEffectStateActive];
   }
   return effectView;
 }
 
 static bool
 ComputeSystemSupportsVibrancy()
 {
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2368,26 +2368,34 @@ nsChildView::UpdateVibrancy(const nsTArr
   if (!VibrancyManager::SystemSupportsVibrancy()) {
     return;
   }
 
   nsIntRegion vibrantLightRegion =
     GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MAC_VIBRANCY_LIGHT);
   nsIntRegion vibrantDarkRegion =
     GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MAC_VIBRANCY_DARK);
+  nsIntRegion menuRegion =
+    GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MENUPOPUP).OrWith(
+      GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MENUITEM).OrWith(
+        GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_CHECKMENUITEM)));
   nsIntRegion tooltipRegion =
     GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_TOOLTIP);
 
   vibrantDarkRegion.SubOut(vibrantLightRegion);
+  vibrantDarkRegion.SubOut(menuRegion);
   vibrantDarkRegion.SubOut(tooltipRegion);
+  vibrantLightRegion.SubOut(menuRegion);
   vibrantLightRegion.SubOut(tooltipRegion);
+  menuRegion.SubOut(tooltipRegion);
 
   auto& vm = EnsureVibrancyManager();
   vm.UpdateVibrantRegion(VibrancyType::LIGHT, vibrantLightRegion);
   vm.UpdateVibrantRegion(VibrancyType::TOOLTIP, tooltipRegion);
+  vm.UpdateVibrantRegion(VibrancyType::MENU, menuRegion);
   vm.UpdateVibrantRegion(VibrancyType::DARK, vibrantDarkRegion);
 }
 
 void
 nsChildView::ClearVibrantAreas()
 {
   if (VibrancyManager::SystemSupportsVibrancy()) {
     EnsureVibrancyManager().ClearVibrantAreas();
@@ -2399,16 +2407,20 @@ WidgetTypeToVibrancyType(uint8_t aWidget
 {
   switch (aWidgetType) {
     case NS_THEME_MAC_VIBRANCY_LIGHT:
       return VibrancyType::LIGHT;
     case NS_THEME_MAC_VIBRANCY_DARK:
       return VibrancyType::DARK;
     case NS_THEME_TOOLTIP:
       return VibrancyType::TOOLTIP;
+    case NS_THEME_MENUPOPUP:
+    case NS_THEME_MENUITEM:
+    case NS_THEME_CHECKMENUITEM:
+      return VibrancyType::MENU;
     default:
       MOZ_CRASH();
   }
 }
 
 NSColor*
 nsChildView::VibrancyFillColorForWidgetType(uint8_t aWidgetType)
 {
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2251,26 +2251,34 @@ nsNativeThemeCocoa::DrawResizer(CGContex
   RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
                                   IsFrameRTL(aFrame));
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 static void
 DrawVibrancyBackground(CGContextRef cgContext, CGRect inBoxRect,
-                       nsIFrame* aFrame, uint8_t aWidgetType)
+                       nsIFrame* aFrame, uint8_t aWidgetType,
+                       int aCornerRadius = 0)
 {
   ChildView* childView = ChildViewForFrame(aFrame);
   if (childView) {
+    NSRect rect = NSRectFromCGRect(inBoxRect);
     NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
+    [NSGraphicsContext saveGraphicsState];
+
+    if (aCornerRadius > 0) {
+      [[NSBezierPath bezierPathWithRoundedRect:rect xRadius:aCornerRadius yRadius:aCornerRadius] addClip];
+    }
 
     [[childView vibrancyFillColorForWidgetType:aWidgetType] set];
-    NSRectFill(NSRectFromCGRect(inBoxRect));
-
+    NSRectFill(rect);
+
+    [NSGraphicsContext restoreGraphicsState];
     [NSGraphicsContext setCurrentContext:savedContext];
   }
 }
 
 static bool
 ScrollbarTrackAndThumbDrawSeparately()
 {
   return nsLookAndFeel::UseOverlayScrollbars() || nsCocoaFeatures::OnLionOrLater();
@@ -2376,68 +2384,69 @@ nsNativeThemeCocoa::DrawWidgetBackground
 
   switch (aWidgetType) {
     case NS_THEME_DIALOG: {
       HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION);
       CGContextFillRect(cgContext, macRect);
     }
       break;
 
-    case NS_THEME_MENUPOPUP: {
-      HIThemeMenuDrawInfo mdi;
-      memset(&mdi, 0, sizeof(mdi));
-      mdi.version = 0;
-      mdi.menuType = IsDisabled(aFrame, eventState) ?
-                       static_cast<ThemeMenuType>(kThemeMenuTypeInactive) :
-                       static_cast<ThemeMenuType>(kThemeMenuTypePopUp);
-
-      bool isLeftOfParent = false;
-      if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) {
-        mdi.menuType = kThemeMenuTypeHierarchical;
+    case NS_THEME_MENUPOPUP:
+      if (VibrancyManager::SystemSupportsVibrancy()) {
+        DrawVibrancyBackground(cgContext, macRect, aFrame, aWidgetType, 4);
+      } else {
+        HIThemeMenuDrawInfo mdi;
+        memset(&mdi, 0, sizeof(mdi));
+        mdi.version = 0;
+        mdi.menuType = IsDisabled(aFrame, eventState) ?
+                         static_cast<ThemeMenuType>(kThemeMenuTypeInactive) :
+                         static_cast<ThemeMenuType>(kThemeMenuTypePopUp);
+
+        bool isLeftOfParent = false;
+        if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) {
+          mdi.menuType = kThemeMenuTypeHierarchical;
+        }
+
+        // The rounded corners draw outside the frame.
+        CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4,
+                                         macRect.size.width, macRect.size.height - 8);
+        HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION);
       }
-      
-      // The rounded corners draw outside the frame.
-      CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4,
-                                       macRect.size.width, macRect.size.height - 8);
-      HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION);
-    }
       break;
 
     case NS_THEME_MENUARROW: {
       bool isRTL = IsFrameRTL(aFrame);
       DrawMenuIcon(cgContext, macRect, eventState, aFrame, kMenuarrowSize,
                    isRTL ? kMenuarrowLeftImage : kMenuarrowRightImage, true);
     }
       break;
 
     case NS_THEME_MENUITEM:
     case NS_THEME_CHECKMENUITEM: {
-      SurfaceFormat format  = thebesCtx->GetDrawTarget()->GetFormat();
-      bool isTransparent = (format == SurfaceFormat::R8G8B8A8) ||
-                           (format == SurfaceFormat::B8G8R8A8);
-      if (isTransparent) {
-        // Clear the background to get correct transparency.
-        CGContextClearRect(cgContext, macRect);
+      bool isDisabled = IsDisabled(aFrame, eventState);
+      bool isSelected = !isDisabled && CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
+      if (!isSelected && VibrancyManager::SystemSupportsVibrancy()) {
+        DrawVibrancyBackground(cgContext, macRect, aFrame, aWidgetType);
+      } else {
+        // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain?
+        HIThemeMenuItemDrawInfo drawInfo;
+        memset(&drawInfo, 0, sizeof(drawInfo));
+        drawInfo.version = 0;
+        drawInfo.itemType = kThemeMenuItemPlain;
+        drawInfo.state = (isDisabled ?
+                           static_cast<ThemeMenuState>(kThemeMenuDisabled) :
+                           isSelected ?
+                             static_cast<ThemeMenuState>(kThemeMenuSelected) :
+                             static_cast<ThemeMenuState>(kThemeMenuActive));
+
+        // XXX pass in the menu rect instead of always using the item rect
+        HIRect ignored;
+        HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored);
       }
 
-      // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain?
-      HIThemeMenuItemDrawInfo drawInfo;
-      memset(&drawInfo, 0, sizeof(drawInfo));
-      drawInfo.version = 0;
-      drawInfo.itemType = kThemeMenuItemPlain;
-      drawInfo.state = (IsDisabled(aFrame, eventState) ?
-                         static_cast<ThemeMenuState>(kThemeMenuDisabled) :
-                         CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ?
-                           static_cast<ThemeMenuState>(kThemeMenuSelected) :
-                           static_cast<ThemeMenuState>(kThemeMenuActive));
-
-      // XXX pass in the menu rect instead of always using the item rect
-      HIRect ignored;
-      HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored);
-
       if (aWidgetType == NS_THEME_CHECKMENUITEM) {
         DrawMenuIcon(cgContext, macRect, eventState, aFrame, kCheckmarkSize, kCheckmarkImage, false);
       }
     }
       break;
 
     case NS_THEME_MENUSEPARATOR: {
       ThemeMenuState menuState;
@@ -3746,16 +3755,19 @@ nsNativeThemeCocoa::WidgetAppearanceDepe
 
 bool
 nsNativeThemeCocoa::NeedToClearBackgroundBehindWidget(uint8_t aWidgetType)
 {
   switch (aWidgetType) {
     case NS_THEME_MAC_VIBRANCY_LIGHT:
     case NS_THEME_MAC_VIBRANCY_DARK:
     case NS_THEME_TOOLTIP:
+    case NS_THEME_MENUPOPUP:
+    case NS_THEME_MENUITEM:
+    case NS_THEME_CHECKMENUITEM:
       return true;
     default:
       return false;
   }
 }
 
 static nscolor ConvertNSColor(NSColor* aColor)
 {
@@ -3770,16 +3782,19 @@ bool
 nsNativeThemeCocoa::WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame,
                                                                uint8_t aWidgetType,
                                                                nscolor* aColor)
 {
   switch (aWidgetType) {
     case NS_THEME_MAC_VIBRANCY_LIGHT:
     case NS_THEME_MAC_VIBRANCY_DARK:
     case NS_THEME_TOOLTIP:
+    case NS_THEME_MENUPOPUP:
+    case NS_THEME_MENUITEM:
+    case NS_THEME_CHECKMENUITEM:
     {
       ChildView* childView = ChildViewForFrame(aFrame);
       if (childView) {
         NSColor* color = [childView vibrancyFontSmoothingBackgroundColorForWidgetType:aWidgetType];
         *aColor = ConvertNSColor(color);
         return true;
       }
       return false;