Index: chrome/browser/ui/cocoa/tabs/tab_view.mm |
diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.mm b/chrome/browser/ui/cocoa/tabs/tab_view.mm |
index cad76744d3ea0f3e22403f3653185a39d494e10a..bd5b053e29234616eecb871ae3edf81d6198cd21 100644 |
--- a/chrome/browser/ui/cocoa/tabs/tab_view.mm |
+++ b/chrome/browser/ui/cocoa/tabs/tab_view.mm |
@@ -19,12 +19,12 @@ |
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFieldCell.h" |
#import "ui/base/cocoa/nsgraphics_context_additions.h" |
#import "ui/base/cocoa/nsview_additions.h" |
+#include "ui/base/cocoa/three_part_image.h" |
#include "ui/base/l10n/l10n_util.h" |
#include "ui/base/resource/resource_bundle.h" |
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
-const int kMaskHeight = 29; // Height of the mask bitmap. |
const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. |
// The amount of time in seconds during which each type of glow increases, holds |
@@ -44,12 +44,30 @@ const NSTimeInterval kGlowUpdateInterval = 0.025; |
// has moved less than the threshold, we want to close the tab. |
const CGFloat kRapidCloseDist = 2.5; |
+namespace { |
+ |
+ui::ThreePartImage* GetMaskImage() { |
+ static ui::ThreePartImage* mask = |
+ new ui::ThreePartImage(IDR_TAB_ALPHA_LEFT, 0, IDR_TAB_ALPHA_RIGHT); |
Nico
2015/02/13 04:17:27
nit: We have CR_DEFINE_STATIC_LOCAL for this (also
|
+ return mask; |
+} |
+ |
+ui::ThreePartImage* GetStrokeImage(bool active) { |
+ static ui::ThreePartImage* activeStroke = new ui::ThreePartImage( |
+ IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT); |
+ static ui::ThreePartImage* inactiveStroke = new ui::ThreePartImage( |
+ IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT); |
+ |
+ return active ? activeStroke : inactiveStroke; |
+} |
+ |
+} // namespace |
+ |
@interface TabView(Private) |
- (void)resetLastGlowUpdateTime; |
- (NSTimeInterval)timeElapsedSinceLastGlowUpdate; |
- (void)adjustGlowValue; |
-- (CGImageRef)tabClippingMask; |
@end // TabView(Private) |
@@ -82,6 +100,8 @@ const CGFloat kRapidCloseDist = 2.5; |
[labelCell setFont:font]; |
[titleView_ setCell:labelCell]; |
titleViewCell_ = labelCell; |
+ |
+ [self setWantsLayer:YES]; // -drawFill: needs a layer. |
} |
return self; |
} |
@@ -157,34 +177,9 @@ const CGFloat kRapidCloseDist = 2.5; |
return defaultHitTestResult; |
NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; |
- NSRect pointRect = NSMakeRect(viewPoint.x, viewPoint.y, 1, 1); |
- |
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
- NSImage* left = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage(); |
- if (viewPoint.x < [left size].width) { |
- NSRect imageRect = NSMakeRect(0, 0, [left size].width, [left size].height); |
- if ([left hitTestRect:pointRect withImageDestinationRect:imageRect |
- context:nil hints:nil flipped:NO]) { |
- return self; |
- } |
- return nil; |
- } |
- |
- NSImage* right = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage(); |
- CGFloat rightX = NSWidth([self bounds]) - [right size].width; |
- if (viewPoint.x > rightX) { |
- NSRect imageRect = NSMakeRect( |
- rightX, 0, [right size].width, [right size].height); |
- if ([right hitTestRect:pointRect withImageDestinationRect:imageRect |
- context:nil hints:nil flipped:NO]) { |
- return self; |
- } |
- return nil; |
- } |
- |
- if (viewPoint.y < kFillHeight) |
- return self; |
- return nil; |
+ NSRect maskRect = [self bounds]; |
+ maskRect.size.height = kFillHeight; |
+ return GetMaskImage()->HitTest(viewPoint, maskRect) ? self : nil; |
} |
// Returns |YES| if this tab can be torn away into a new window. |
@@ -312,74 +307,48 @@ const CGFloat kRapidCloseDist = 2.5; |
return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); |
} |
-// Draws the active tab background. |
-- (void)drawFillForActiveTab:(NSRect)dirtyRect { |
- NSColor* backgroundImageColor = [self backgroundColorForSelected:YES]; |
- [backgroundImageColor set]; |
- |
- // Themes can have partially transparent images. NSRectFill() is measurably |
- // faster though, so call it for the known-safe default theme. |
- ThemeService* themeProvider = |
- static_cast<ThemeService*>([[self window] themeProvider]); |
- if (themeProvider && themeProvider->UsingDefaultTheme()) |
- NSRectFill(dirtyRect); |
- else |
- NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); |
-} |
- |
// Draws the tab background. |
- (void)drawFill:(NSRect)dirtyRect { |
gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
- NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
- CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
+ NSRect bounds = [self bounds]; |
+ |
+ NSRect clippingRect = bounds; |
+ clippingRect.size.height = kFillHeight; |
+ if (state_ != NSOnState) { |
+ // Background tabs should not paint over the tab strip separator, which is |
+ // two pixels high in both lodpi and hidpi. |
+ clippingRect.origin.y = 2 * [self cr_lineWidth]; |
+ clippingRect.size.height -= clippingRect.origin.y; |
+ } |
+ NSRectClip(clippingRect); |
- ThemeService* themeProvider = |
- static_cast<ThemeService*>([[self window] themeProvider]); |
NSPoint position = [[self window] |
- themeImagePositionForAlignment: THEME_IMAGE_ALIGN_WITH_TAB_STRIP]; |
- [context cr_setPatternPhase:position forView:self]; |
+ themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP]; |
+ [[NSGraphicsContext currentContext] cr_setPatternPhase:position forView:self]; |
- CGImageRef mask([self tabClippingMask]); |
- CGRect maskBounds = CGRectMake(0, 0, maskCacheWidth_, kMaskHeight); |
- CGContextClipToMask(cgContext, maskBounds, mask); |
+ [[self backgroundColorForSelected:(state_ != NSOffState)] set]; |
+ NSRectFill(dirtyRect); |
- // There is only 1 active tab at a time. |
- // It has a different fill color which draws over the separator line. |
- if (state_ == NSOnState) { |
- [self drawFillForActiveTab:dirtyRect]; |
- return; |
- } |
- |
- // Background tabs should not paint over the tab strip separator, which is |
- // two pixels high in both lodpi and hidpi. |
- if (dirtyRect.origin.y < 1) |
- dirtyRect.origin.y = 2 * [self cr_lineWidth]; |
+ if (state_ == NSOffState) |
+ [self drawGlow:dirtyRect]; |
- // There can be multiple selected tabs. |
- // They have the same fill color as the active tab, but do not draw over |
- // the separator. |
- if (state_ == NSMixedState) { |
- [self drawFillForActiveTab:dirtyRect]; |
- return; |
+ // If we filled outside the middle rect, we need to erase what we filled |
+ // outside the tab's shape. |
+ // This only works if we are drawing to our own backing layer. |
+ if (!NSContainsRect(GetMaskImage()->GetMiddleRect(bounds), dirtyRect)) { |
+ DCHECK([self layer]); |
+ GetMaskImage()->DrawInRect(bounds, NSCompositeDestinationIn, 1.0); |
} |
+} |
- // Draw the tab background. |
- NSColor* backgroundImageColor = [self backgroundColorForSelected:NO]; |
- [backgroundImageColor set]; |
- |
- // Themes can have partially transparent images. NSRectFill() is measurably |
- // faster though, so call it for the known-safe default theme. |
- bool usingDefaultTheme = themeProvider && themeProvider->UsingDefaultTheme(); |
- if (usingDefaultTheme) |
- NSRectFill(dirtyRect); |
- else |
- NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); |
+// Draw the glow for hover and the overlay for alerts. |
+- (void)drawGlow:(NSRect)dirtyRect { |
+ NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
+ CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
- // Draw the glow for hover and the overlay for alerts. |
CGFloat hoverAlpha = [self hoverAlpha]; |
CGFloat alertAlpha = [self alertAlpha]; |
if (hoverAlpha > 0 || alertAlpha > 0) { |
- gfx::ScopedNSGraphicsContextSaveGState contextSave; |
CGContextBeginTransparencyLayer(cgContext, 0); |
// The alert glow overlay is like the selected state but at most at most 80% |
@@ -388,12 +357,14 @@ const CGFloat kRapidCloseDist = 2.5; |
backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; |
CGContextSetAlpha(cgContext, backgroundAlpha); |
- [self drawFillForActiveTab:dirtyRect]; |
+ [[self backgroundColorForSelected:YES] set]; |
+ NSRectFill(dirtyRect); |
// ui::ThemeProvider::HasCustomImage is true only if the theme provides the |
// image. However, even if the theme doesn't provide a tab background, the |
// theme machinery will make one if given a frame image. See |
// BrowserThemePack::GenerateTabBackgroundImages for details. |
+ ui::ThemeProvider* themeProvider = [[self window] themeProvider]; |
BOOL hasCustomTheme = themeProvider && |
(themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || |
themeProvider->HasCustomImage(IDR_THEME_FRAME)); |
@@ -422,35 +393,12 @@ const CGFloat kRapidCloseDist = 2.5; |
// Draws the tab outline. |
- (void)drawStroke:(NSRect)dirtyRect { |
- BOOL focused = [[self window] isMainWindow]; |
- CGFloat alpha = focused ? 1.0 : tabs::kImageNoFocusAlpha; |
- |
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
- float height = |
- [rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage() size].height; |
- if (state_ == NSOnState) { |
- NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height), |
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage(), |
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_CENTER).ToNSImage(), |
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_RIGHT).ToNSImage(), |
- /*vertical=*/NO, |
- NSCompositeSourceOver, |
- alpha, |
- /*flipped=*/NO); |
- } else { |
- NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height), |
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_LEFT).ToNSImage(), |
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_CENTER).ToNSImage(), |
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_RIGHT).ToNSImage(), |
- /*vertical=*/NO, |
- NSCompositeSourceOver, |
- alpha, |
- /*flipped=*/NO); |
- } |
+ CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha; |
+ GetStrokeImage(state_ == NSOnState) |
+ ->DrawInRect([self bounds], NSCompositeSourceOver, alpha); |
} |
- (void)drawRect:(NSRect)dirtyRect { |
- // Close button and image are drawn by subviews. |
[self drawFill:dirtyRect]; |
[self drawStroke:dirtyRect]; |
@@ -749,57 +697,4 @@ const CGFloat kRapidCloseDist = 2.5; |
[self setNeedsDisplay:YES]; |
} |
-- (CGImageRef)tabClippingMask { |
- // NOTE: NSHeight([self bounds]) doesn't match the height of the bitmaps. |
- CGFloat scale = 1; |
- if ([[self window] respondsToSelector:@selector(backingScaleFactor)]) |
- scale = [[self window] backingScaleFactor]; |
- |
- NSRect bounds = [self bounds]; |
- CGFloat tabWidth = NSWidth(bounds); |
- if (tabWidth == maskCacheWidth_ && scale == maskCacheScale_) |
- return maskCache_.get(); |
- |
- maskCacheWidth_ = tabWidth; |
- maskCacheScale_ = scale; |
- |
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
- NSImage* leftMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage(); |
- NSImage* rightMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage(); |
- |
- CGFloat leftWidth = leftMask.size.width; |
- CGFloat rightWidth = rightMask.size.width; |
- |
- // Image masks must be in the DeviceGray colorspace. Create a context and |
- // draw the mask into it. |
- base::ScopedCFTypeRef<CGColorSpaceRef> colorspace( |
- CGColorSpaceCreateDeviceGray()); |
- base::ScopedCFTypeRef<CGContextRef> maskContext( |
- CGBitmapContextCreate(NULL, tabWidth * scale, kMaskHeight * scale, |
- 8, tabWidth * scale, colorspace, 0)); |
- CGContextScaleCTM(maskContext, scale, scale); |
- NSGraphicsContext* maskGraphicsContext = |
- [NSGraphicsContext graphicsContextWithGraphicsPort:maskContext |
- flipped:NO]; |
- |
- gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
- [NSGraphicsContext setCurrentContext:maskGraphicsContext]; |
- |
- // Draw mask image. |
- [[NSColor blackColor] setFill]; |
- CGContextFillRect(maskContext, CGRectMake(0, 0, tabWidth, kMaskHeight)); |
- |
- NSDrawThreePartImage(NSMakeRect(0, 0, tabWidth, kMaskHeight), |
- leftMask, nil, rightMask, /*vertical=*/NO, NSCompositeSourceOver, 1.0, |
- /*flipped=*/NO); |
- |
- CGFloat middleWidth = tabWidth - leftWidth - rightWidth; |
- NSRect middleRect = NSMakeRect(leftWidth, 0, middleWidth, kFillHeight); |
- [[NSColor whiteColor] setFill]; |
- NSRectFill(middleRect); |
- |
- maskCache_.reset(CGBitmapContextCreateImage(maskContext)); |
- return maskCache_; |
-} |
- |
@end // @implementation TabView(Private) |