Chromium Code Reviews| Index: chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm |
| diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm |
| index 4b32010d44e65a67c702b90540b9b1f7ec77acf8..5d750e708a3824611950793a0596bfc9725c18c4 100644 |
| --- a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm |
| +++ b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm |
| @@ -4,21 +4,43 @@ |
| #import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h" |
| +#include "chrome/browser/themes/theme_properties.h" |
| #import "chrome/browser/ui/cocoa/framed_browser_window.h" |
| #import "ui/base/cocoa/nsview_additions.h" |
| +#include "ui/base/theme_provider.h" |
| -@implementation TabStripBackgroundView |
| +// TODO: Remove once we no longer support 10.9 (-Wunguarded-availability) |
|
Robert Sesek
2017/01/13 19:39:41
nit: Format is TODO(xxx) where "xxx" is a crbug UR
Sidney San Martín
2017/01/13 20:05:17
Done.
|
| +@class NSVisualEffectView; |
| +extern NSString* const NSAppearanceNameVibrantDark; |
| +extern NSString* const NSAppearanceNameVibrantLight; |
| + |
| +@interface NSAppearance (AllowsVibrancy) |
| +@property(readonly) BOOL allowsVibrancy; |
| +@end |
| +// /TODO |
| + |
| +@interface TabStripThemeBackgroundView : NSView |
| +@property(nonatomic) BOOL inATabDraggingOverlayWindow; |
| +@end |
| + |
| +@implementation TabStripThemeBackgroundView |
| + |
| +@synthesize inATabDraggingOverlayWindow = inATabDraggingOverlayWindow_; |
| - (void)drawRect:(NSRect)dirtyRect { |
| // Only the top corners are rounded. For simplicity, round all 4 corners but |
| // draw the bottom corners outside of the visible bounds. |
| float cornerRadius = 4.0; |
| + bool isFullscreen = (self.window.styleMask & NSFullScreenWindowMask) != 0; |
| + |
| NSRect roundedRect = [self bounds]; |
| - roundedRect.origin.y -= cornerRadius; |
| - roundedRect.size.height += cornerRadius; |
| - [[NSBezierPath bezierPathWithRoundedRect:roundedRect |
| - xRadius:cornerRadius |
| - yRadius:cornerRadius] addClip]; |
| + if (!isFullscreen) { |
| + roundedRect.origin.y -= cornerRadius; |
| + roundedRect.size.height += cornerRadius; |
| + [[NSBezierPath bezierPathWithRoundedRect:roundedRect |
| + xRadius:cornerRadius |
| + yRadius:cornerRadius] addClip]; |
| + } |
| BOOL themed = [FramedBrowserWindow drawWindowThemeInDirtyRect:dirtyRect |
| forView:self |
| bounds:roundedRect |
| @@ -26,28 +48,151 @@ |
| // Draw a 1px border on the top edge and top corners. |
| if (themed) { |
| - CGFloat lineWidth = [self cr_lineWidth]; |
| - // Inset the vertical lines by 0.5px so that the top line gets a full pixel. |
| - // Outset the horizontal lines by 0.5px so that they are not visible, but |
| - // still get the rounded corners to get a border. |
| - NSRect strokeRect = NSInsetRect(roundedRect, -lineWidth/2, lineWidth/2); |
| - NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:strokeRect |
| - xRadius:cornerRadius |
| - yRadius:cornerRadius]; |
| - [path setLineWidth:lineWidth]; |
| - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.5] set]; |
| - [path stroke]; |
| + if (!isFullscreen) { |
| + CGFloat lineWidth = [self cr_lineWidth]; |
| + // Inset the vertical lines by 0.5px so that the top line gets a full |
| + // pixel. Outset the horizontal lines by 0.5px so that they are not |
| + // visible, but still get the rounded corners to get a border. |
| + NSRect strokeRect = |
| + NSInsetRect(roundedRect, -lineWidth / 2, lineWidth / 2); |
| + NSBezierPath* path = |
| + [NSBezierPath bezierPathWithRoundedRect:strokeRect |
| + xRadius:cornerRadius |
| + yRadius:cornerRadius]; |
| + [path setLineWidth:lineWidth]; |
| + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.5] set]; |
| + [path stroke]; |
| + } |
| + } else if (!inATabDraggingOverlayWindow_) { |
| + // If the window is not themed, and not being used to drag tabs between |
| + // browser windows, decrease the tab strip background's translucency by |
| + // overlaying it with a partially-transparent gray. The gray is somewhat |
| + // opaque for Incognito mode, very opaque for non-Incognito mode, and |
| + // completely opaque when the window is not active. |
| + if (const ui::ThemeProvider* themeProvider = |
| + [[self window] themeProvider]) { |
| + NSColor* overlayColor = nil; |
| + if (self.window.isMainWindow) { |
| + NSAppearance* appearance = self.effectiveAppearance; |
| + if ([appearance respondsToSelector:@selector(allowsVibrancy)] && |
| + appearance.allowsVibrancy) { |
| + overlayColor = themeProvider->GetNSColor( |
| + ThemeProperties::COLOR_FRAME_VIBRANCY_OVERLAY); |
| + } else if (themeProvider->InIncognitoMode()) { |
| + overlayColor = [NSColor colorWithSRGBRed:20 / 255. |
| + green:22 / 255. |
| + blue:24 / 255. |
| + alpha:1]; |
| + } else { |
| + overlayColor = |
| + themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME); |
| + } |
| + } else { |
| + overlayColor = |
| + themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME_INACTIVE); |
| + } |
| + |
| + if (overlayColor) { |
| + [overlayColor set]; |
| + NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); |
| + } |
| + } |
| } |
| } |
| +@end |
| + |
| +@implementation TabStripBackgroundView { |
| + // Weak, owned by its superview. |
| + TabStripThemeBackgroundView* themeBackgroundView_; |
| + |
| + // Weak, owned by its superview. |
| + NSVisualEffectView* visualEffectView_; |
| +} |
| + |
| +- (instancetype)initWithFrame:(NSRect)frame { |
| + if ((self = [super initWithFrame:frame])) { |
| + themeBackgroundView_ = [[[TabStripThemeBackgroundView alloc] |
| + initWithFrame:self.bounds] autorelease]; |
| + themeBackgroundView_.autoresizingMask = |
| + NSViewWidthSizable | NSViewHeightSizable; |
| + [self addSubview:themeBackgroundView_]; |
| + } |
| + return self; |
| +} |
| + |
| +- (BOOL)inATabDraggingOverlayWindow { |
| + return themeBackgroundView_.inATabDraggingOverlayWindow; |
| +} |
| + |
| +- (void)setInATabDraggingOverlayWindow:(BOOL)inATabDraggingOverlayWindow { |
| + themeBackgroundView_.inATabDraggingOverlayWindow = |
| + inATabDraggingOverlayWindow; |
| +} |
| + |
| +- (void)updateVisualEffectView { |
| + const ui::ThemeProvider* themeProvider = [[self window] themeProvider]; |
| + const bool isFullscreen = |
| + (self.window.styleMask & NSFullScreenWindowMask) != 0; |
| + |
| + // Visual effects cause higher power consumption in full screen. |
| + if (!isFullscreen && themeProvider->UsingSystemTheme()) { |
| + if (!visualEffectView_) { |
| + visualEffectView_ = |
| + [[[NSVisualEffectView alloc] initWithFrame:self.bounds] autorelease]; |
| + if (!visualEffectView_) |
| + return; |
| + |
| + visualEffectView_.autoresizingMask = |
| + NSViewWidthSizable | NSViewHeightSizable; |
| + visualEffectView_.appearance = |
| + [NSAppearance appearanceNamed:themeProvider->InIncognitoMode() |
| + ? NSAppearanceNameVibrantDark |
| + : NSAppearanceNameVibrantLight]; |
| + [self addSubview:visualEffectView_]; |
| + [visualEffectView_ addSubview:themeBackgroundView_]; |
| + } |
| + } else { |
| + if (visualEffectView_) { |
| + [self addSubview:themeBackgroundView_]; |
| + [visualEffectView_ removeFromSuperview]; |
| + visualEffectView_ = nil; |
| + } |
| + } |
| +} |
| + |
| +- (void)viewWillMoveToWindow:(NSWindow*)newWindow { |
| + // AppKit calls this method when the view's position in the view hierarchy |
| + // changes, even if its parent window won't change. |
| + if (newWindow == self.window) |
| + return; |
| + if (self.window) |
| + [self.window removeObserver:self forKeyPath:@"styleMask"]; |
| + if (newWindow) |
| + [newWindow addObserver:self forKeyPath:@"styleMask" options:0 context:nil]; |
| +} |
| + |
| +- (void)observeValueForKeyPath:(NSString*)keyPath |
| + ofObject:(id)object |
| + change:(NSDictionary*)change |
| + context:(void*)context { |
| + DCHECK_EQ(object, self.window); |
| + DCHECK([keyPath isEqualToString:@"styleMask"]); |
| + [self updateVisualEffectView]; |
| +} |
| // ThemedWindowDrawing implementation. |
| - (void)windowDidChangeTheme { |
| - [self setNeedsDisplay:YES]; |
| + [self updateVisualEffectView]; |
| + [themeBackgroundView_ setNeedsDisplay:YES]; |
| } |
| - (void)windowDidChangeActive { |
| - [self setNeedsDisplay:YES]; |
| + // TODO(sdy): It shouldn't be necessary to update the visual effect view |
| + // here, since it only changes when the theme changes), but the window isn't |
| + // associated with a themeProvider at init time. |
| + [self updateVisualEffectView]; |
| + [themeBackgroundView_ setNeedsDisplay:YES]; |
| } |
| @end |