| 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);
|
| + 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)
|
|
|