| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/tabs/tab_view.h" | 5 #import "chrome/browser/ui/cocoa/tabs/tab_view.h" |
| 6 | 6 |
| 7 #include "base/i18n/rtl.h" | 7 #include "base/i18n/rtl.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/mac/sdk_forward_declarations.h" | 9 #include "base/mac/sdk_forward_declarations.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| 11 #include "chrome/browser/themes/theme_service.h" | 11 #include "chrome/browser/themes/theme_service.h" |
| 12 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button_cocoa.h" | 12 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button_cocoa.h" |
| 13 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" | 13 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" |
| 14 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h" |
| 15 #import "chrome/browser/ui/cocoa/themed_window.h" | 15 #import "chrome/browser/ui/cocoa/themed_window.h" |
| 16 #import "chrome/browser/ui/cocoa/view_id_util.h" | 16 #import "chrome/browser/ui/cocoa/view_id_util.h" |
| 17 #include "chrome/grit/generated_resources.h" | 17 #include "chrome/grit/generated_resources.h" |
| 18 #include "grit/theme_resources.h" | 18 #include "grit/theme_resources.h" |
| 19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFiel
dCell.h" | 19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFiel
dCell.h" |
| 20 #import "ui/base/cocoa/nsgraphics_context_additions.h" | 20 #import "ui/base/cocoa/nsgraphics_context_additions.h" |
| 21 #import "ui/base/cocoa/nsview_additions.h" | 21 #import "ui/base/cocoa/nsview_additions.h" |
| 22 #include "ui/base/cocoa/three_part_image.h" |
| 22 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" |
| 23 #include "ui/base/resource/resource_bundle.h" | 24 #include "ui/base/resource/resource_bundle.h" |
| 24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | 25 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
| 25 | 26 |
| 26 | 27 |
| 27 const int kMaskHeight = 29; // Height of the mask bitmap. | |
| 28 const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. | 28 const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. |
| 29 | 29 |
| 30 // The amount of time in seconds during which each type of glow increases, holds | 30 // The amount of time in seconds during which each type of glow increases, holds |
| 31 // steady, and decreases, respectively. | 31 // steady, and decreases, respectively. |
| 32 const NSTimeInterval kHoverShowDuration = 0.2; | 32 const NSTimeInterval kHoverShowDuration = 0.2; |
| 33 const NSTimeInterval kHoverHoldDuration = 0.02; | 33 const NSTimeInterval kHoverHoldDuration = 0.02; |
| 34 const NSTimeInterval kHoverHideDuration = 0.4; | 34 const NSTimeInterval kHoverHideDuration = 0.4; |
| 35 const NSTimeInterval kAlertShowDuration = 0.4; | 35 const NSTimeInterval kAlertShowDuration = 0.4; |
| 36 const NSTimeInterval kAlertHoldDuration = 0.4; | 36 const NSTimeInterval kAlertHoldDuration = 0.4; |
| 37 const NSTimeInterval kAlertHideDuration = 0.4; | 37 const NSTimeInterval kAlertHideDuration = 0.4; |
| 38 | 38 |
| 39 // The default time interval in seconds between glow updates (when | 39 // The default time interval in seconds between glow updates (when |
| 40 // increasing/decreasing). | 40 // increasing/decreasing). |
| 41 const NSTimeInterval kGlowUpdateInterval = 0.025; | 41 const NSTimeInterval kGlowUpdateInterval = 0.025; |
| 42 | 42 |
| 43 // This is used to judge whether the mouse has moved during rapid closure; if it | 43 // This is used to judge whether the mouse has moved during rapid closure; if it |
| 44 // has moved less than the threshold, we want to close the tab. | 44 // has moved less than the threshold, we want to close the tab. |
| 45 const CGFloat kRapidCloseDist = 2.5; | 45 const CGFloat kRapidCloseDist = 2.5; |
| 46 | 46 |
| 47 namespace { |
| 48 |
| 49 ui::ThreePartImage* GetMaskImage() { |
| 50 static ui::ThreePartImage* mask = |
| 51 new ui::ThreePartImage(IDR_TAB_ALPHA_LEFT, 0, IDR_TAB_ALPHA_RIGHT); |
| 52 return mask; |
| 53 } |
| 54 |
| 55 ui::ThreePartImage* GetStrokeImage(bool active) { |
| 56 static ui::ThreePartImage* activeStroke = new ui::ThreePartImage( |
| 57 IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT); |
| 58 static ui::ThreePartImage* inactiveStroke = new ui::ThreePartImage( |
| 59 IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT); |
| 60 |
| 61 return active ? activeStroke : inactiveStroke; |
| 62 } |
| 63 |
| 64 } // namespace |
| 65 |
| 47 @interface TabView(Private) | 66 @interface TabView(Private) |
| 48 | 67 |
| 49 - (void)resetLastGlowUpdateTime; | 68 - (void)resetLastGlowUpdateTime; |
| 50 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate; | 69 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate; |
| 51 - (void)adjustGlowValue; | 70 - (void)adjustGlowValue; |
| 52 - (CGImageRef)tabClippingMask; | |
| 53 | 71 |
| 54 @end // TabView(Private) | 72 @end // TabView(Private) |
| 55 | 73 |
| 56 @implementation TabView | 74 @implementation TabView |
| 57 | 75 |
| 58 @synthesize state = state_; | 76 @synthesize state = state_; |
| 59 @synthesize hoverAlpha = hoverAlpha_; | 77 @synthesize hoverAlpha = hoverAlpha_; |
| 60 @synthesize alertAlpha = alertAlpha_; | 78 @synthesize alertAlpha = alertAlpha_; |
| 61 @synthesize closing = closing_; | 79 @synthesize closing = closing_; |
| 62 | 80 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 75 [titleView_ setAutoresizingMask:NSViewWidthSizable]; | 93 [titleView_ setAutoresizingMask:NSViewWidthSizable]; |
| 76 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( | 94 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( |
| 77 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); | 95 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); |
| 78 [labelCell setControlSize:NSSmallControlSize]; | 96 [labelCell setControlSize:NSSmallControlSize]; |
| 79 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; | 97 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; |
| 80 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName] | 98 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName] |
| 81 size:fontSize]; | 99 size:fontSize]; |
| 82 [labelCell setFont:font]; | 100 [labelCell setFont:font]; |
| 83 [titleView_ setCell:labelCell]; | 101 [titleView_ setCell:labelCell]; |
| 84 titleViewCell_ = labelCell; | 102 titleViewCell_ = labelCell; |
| 103 |
| 104 [self setWantsLayer:YES]; // -drawFill: needs a layer. |
| 85 } | 105 } |
| 86 return self; | 106 return self; |
| 87 } | 107 } |
| 88 | 108 |
| 89 - (void)dealloc { | 109 - (void)dealloc { |
| 90 // Cancel any delayed requests that may still be pending (drags or hover). | 110 // Cancel any delayed requests that may still be pending (drags or hover). |
| 91 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 111 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 92 [super dealloc]; | 112 [super dealloc]; |
| 93 } | 113 } |
| 94 | 114 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 } | 170 } |
| 151 | 171 |
| 152 // Determines which view a click in our frame actually hit. It's either this | 172 // Determines which view a click in our frame actually hit. It's either this |
| 153 // view or one of the child buttons. | 173 // view or one of the child buttons. |
| 154 - (NSView*)hitTest:(NSPoint)aPoint { | 174 - (NSView*)hitTest:(NSPoint)aPoint { |
| 155 NSView* const defaultHitTestResult = [super hitTest:aPoint]; | 175 NSView* const defaultHitTestResult = [super hitTest:aPoint]; |
| 156 if ([defaultHitTestResult isKindOfClass:[NSButton class]]) | 176 if ([defaultHitTestResult isKindOfClass:[NSButton class]]) |
| 157 return defaultHitTestResult; | 177 return defaultHitTestResult; |
| 158 | 178 |
| 159 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; | 179 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; |
| 160 NSRect pointRect = NSMakeRect(viewPoint.x, viewPoint.y, 1, 1); | 180 NSRect maskRect = [self bounds]; |
| 161 | 181 maskRect.size.height = kFillHeight; |
| 162 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 182 return GetMaskImage()->HitTest(viewPoint, maskRect) ? self : nil; |
| 163 NSImage* left = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage(); | |
| 164 if (viewPoint.x < [left size].width) { | |
| 165 NSRect imageRect = NSMakeRect(0, 0, [left size].width, [left size].height); | |
| 166 if ([left hitTestRect:pointRect withImageDestinationRect:imageRect | |
| 167 context:nil hints:nil flipped:NO]) { | |
| 168 return self; | |
| 169 } | |
| 170 return nil; | |
| 171 } | |
| 172 | |
| 173 NSImage* right = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage(); | |
| 174 CGFloat rightX = NSWidth([self bounds]) - [right size].width; | |
| 175 if (viewPoint.x > rightX) { | |
| 176 NSRect imageRect = NSMakeRect( | |
| 177 rightX, 0, [right size].width, [right size].height); | |
| 178 if ([right hitTestRect:pointRect withImageDestinationRect:imageRect | |
| 179 context:nil hints:nil flipped:NO]) { | |
| 180 return self; | |
| 181 } | |
| 182 return nil; | |
| 183 } | |
| 184 | |
| 185 if (viewPoint.y < kFillHeight) | |
| 186 return self; | |
| 187 return nil; | |
| 188 } | 183 } |
| 189 | 184 |
| 190 // Returns |YES| if this tab can be torn away into a new window. | 185 // Returns |YES| if this tab can be torn away into a new window. |
| 191 - (BOOL)canBeDragged { | 186 - (BOOL)canBeDragged { |
| 192 return [controller_ tabCanBeDragged:controller_]; | 187 return [controller_ tabCanBeDragged:controller_]; |
| 193 } | 188 } |
| 194 | 189 |
| 195 // Handle clicks and drags in this button. We get here because we have | 190 // Handle clicks and drags in this button. We get here because we have |
| 196 // overridden acceptsFirstMouse: and the click is within our bounds. | 191 // overridden acceptsFirstMouse: and the click is within our bounds. |
| 197 - (void)mouseDown:(NSEvent*)theEvent { | 192 - (void)mouseDown:(NSEvent*)theEvent { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 }, | 300 }, |
| 306 }; | 301 }; |
| 307 | 302 |
| 308 // Themes don't have an inactive image so only look for one if there's no | 303 // Themes don't have an inactive image so only look for one if there's no |
| 309 // theme. | 304 // theme. |
| 310 bool active = | 305 bool active = |
| 311 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme(); | 306 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme(); |
| 312 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); | 307 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); |
| 313 } | 308 } |
| 314 | 309 |
| 315 // Draws the active tab background. | |
| 316 - (void)drawFillForActiveTab:(NSRect)dirtyRect { | |
| 317 NSColor* backgroundImageColor = [self backgroundColorForSelected:YES]; | |
| 318 [backgroundImageColor set]; | |
| 319 | |
| 320 // Themes can have partially transparent images. NSRectFill() is measurably | |
| 321 // faster though, so call it for the known-safe default theme. | |
| 322 ThemeService* themeProvider = | |
| 323 static_cast<ThemeService*>([[self window] themeProvider]); | |
| 324 if (themeProvider && themeProvider->UsingDefaultTheme()) | |
| 325 NSRectFill(dirtyRect); | |
| 326 else | |
| 327 NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); | |
| 328 } | |
| 329 | |
| 330 // Draws the tab background. | 310 // Draws the tab background. |
| 331 - (void)drawFill:(NSRect)dirtyRect { | 311 - (void)drawFill:(NSRect)dirtyRect { |
| 332 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 312 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 313 NSRect bounds = [self bounds]; |
| 314 |
| 315 NSRect clippingRect = bounds; |
| 316 clippingRect.size.height = kFillHeight; |
| 317 if (state_ != NSOnState) { |
| 318 // Background tabs should not paint over the tab strip separator, which is |
| 319 // two pixels high in both lodpi and hidpi. |
| 320 clippingRect.origin.y = 2 * [self cr_lineWidth]; |
| 321 clippingRect.size.height -= clippingRect.origin.y; |
| 322 } |
| 323 NSRectClip(clippingRect); |
| 324 |
| 325 NSPoint position = [[self window] |
| 326 themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP]; |
| 327 [[NSGraphicsContext currentContext] cr_setPatternPhase:position forView:self]; |
| 328 |
| 329 [[self backgroundColorForSelected:(state_ != NSOffState)] set]; |
| 330 NSRectFill(dirtyRect); |
| 331 |
| 332 if (state_ == NSOffState) |
| 333 [self drawGlow:dirtyRect]; |
| 334 |
| 335 // If we filled outside the middle rect, we need to erase what we filled |
| 336 // outside the tab's shape. |
| 337 // This only works if we are drawing to our own backing layer. |
| 338 if (!NSContainsRect(GetMaskImage()->GetMiddleRect(bounds), dirtyRect)) { |
| 339 DCHECK([self layer]); |
| 340 GetMaskImage()->DrawInRect(bounds, NSCompositeDestinationIn, 1.0); |
| 341 } |
| 342 } |
| 343 |
| 344 // Draw the glow for hover and the overlay for alerts. |
| 345 - (void)drawGlow:(NSRect)dirtyRect { |
| 333 NSGraphicsContext* context = [NSGraphicsContext currentContext]; | 346 NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
| 334 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); | 347 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
| 335 | 348 |
| 336 ThemeService* themeProvider = | |
| 337 static_cast<ThemeService*>([[self window] themeProvider]); | |
| 338 NSPoint position = [[self window] | |
| 339 themeImagePositionForAlignment: THEME_IMAGE_ALIGN_WITH_TAB_STRIP]; | |
| 340 [context cr_setPatternPhase:position forView:self]; | |
| 341 | |
| 342 CGImageRef mask([self tabClippingMask]); | |
| 343 CGRect maskBounds = CGRectMake(0, 0, maskCacheWidth_, kMaskHeight); | |
| 344 CGContextClipToMask(cgContext, maskBounds, mask); | |
| 345 | |
| 346 // There is only 1 active tab at a time. | |
| 347 // It has a different fill color which draws over the separator line. | |
| 348 if (state_ == NSOnState) { | |
| 349 [self drawFillForActiveTab:dirtyRect]; | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 // Background tabs should not paint over the tab strip separator, which is | |
| 354 // two pixels high in both lodpi and hidpi. | |
| 355 if (dirtyRect.origin.y < 1) | |
| 356 dirtyRect.origin.y = 2 * [self cr_lineWidth]; | |
| 357 | |
| 358 // There can be multiple selected tabs. | |
| 359 // They have the same fill color as the active tab, but do not draw over | |
| 360 // the separator. | |
| 361 if (state_ == NSMixedState) { | |
| 362 [self drawFillForActiveTab:dirtyRect]; | |
| 363 return; | |
| 364 } | |
| 365 | |
| 366 // Draw the tab background. | |
| 367 NSColor* backgroundImageColor = [self backgroundColorForSelected:NO]; | |
| 368 [backgroundImageColor set]; | |
| 369 | |
| 370 // Themes can have partially transparent images. NSRectFill() is measurably | |
| 371 // faster though, so call it for the known-safe default theme. | |
| 372 bool usingDefaultTheme = themeProvider && themeProvider->UsingDefaultTheme(); | |
| 373 if (usingDefaultTheme) | |
| 374 NSRectFill(dirtyRect); | |
| 375 else | |
| 376 NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); | |
| 377 | |
| 378 // Draw the glow for hover and the overlay for alerts. | |
| 379 CGFloat hoverAlpha = [self hoverAlpha]; | 349 CGFloat hoverAlpha = [self hoverAlpha]; |
| 380 CGFloat alertAlpha = [self alertAlpha]; | 350 CGFloat alertAlpha = [self alertAlpha]; |
| 381 if (hoverAlpha > 0 || alertAlpha > 0) { | 351 if (hoverAlpha > 0 || alertAlpha > 0) { |
| 382 gfx::ScopedNSGraphicsContextSaveGState contextSave; | |
| 383 CGContextBeginTransparencyLayer(cgContext, 0); | 352 CGContextBeginTransparencyLayer(cgContext, 0); |
| 384 | 353 |
| 385 // The alert glow overlay is like the selected state but at most at most 80% | 354 // The alert glow overlay is like the selected state but at most at most 80% |
| 386 // opaque. The hover glow brings up the overlay's opacity at most 50%. | 355 // opaque. The hover glow brings up the overlay's opacity at most 50%. |
| 387 CGFloat backgroundAlpha = 0.8 * alertAlpha; | 356 CGFloat backgroundAlpha = 0.8 * alertAlpha; |
| 388 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; | 357 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; |
| 389 CGContextSetAlpha(cgContext, backgroundAlpha); | 358 CGContextSetAlpha(cgContext, backgroundAlpha); |
| 390 | 359 |
| 391 [self drawFillForActiveTab:dirtyRect]; | 360 [[self backgroundColorForSelected:YES] set]; |
| 361 NSRectFill(dirtyRect); |
| 392 | 362 |
| 393 // ui::ThemeProvider::HasCustomImage is true only if the theme provides the | 363 // ui::ThemeProvider::HasCustomImage is true only if the theme provides the |
| 394 // image. However, even if the theme doesn't provide a tab background, the | 364 // image. However, even if the theme doesn't provide a tab background, the |
| 395 // theme machinery will make one if given a frame image. See | 365 // theme machinery will make one if given a frame image. See |
| 396 // BrowserThemePack::GenerateTabBackgroundImages for details. | 366 // BrowserThemePack::GenerateTabBackgroundImages for details. |
| 367 ui::ThemeProvider* themeProvider = [[self window] themeProvider]; |
| 397 BOOL hasCustomTheme = themeProvider && | 368 BOOL hasCustomTheme = themeProvider && |
| 398 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || | 369 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || |
| 399 themeProvider->HasCustomImage(IDR_THEME_FRAME)); | 370 themeProvider->HasCustomImage(IDR_THEME_FRAME)); |
| 400 // Draw a mouse hover gradient for the default themes. | 371 // Draw a mouse hover gradient for the default themes. |
| 401 if (hoverAlpha > 0) { | 372 if (hoverAlpha > 0) { |
| 402 if (themeProvider && !hasCustomTheme) { | 373 if (themeProvider && !hasCustomTheme) { |
| 403 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); | 374 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); |
| 404 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 | 375 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 |
| 405 alpha:1.0 * hoverAlpha] | 376 alpha:1.0 * hoverAlpha] |
| 406 endingColor:[NSColor colorWithCalibratedWhite:1.0 | 377 endingColor:[NSColor colorWithCalibratedWhite:1.0 |
| 407 alpha:0.0]]; | 378 alpha:0.0]]; |
| 408 NSRect rect = [self bounds]; | 379 NSRect rect = [self bounds]; |
| 409 NSPoint point = hoverPoint_; | 380 NSPoint point = hoverPoint_; |
| 410 point.y = NSHeight(rect); | 381 point.y = NSHeight(rect); |
| 411 [glow drawFromCenter:point | 382 [glow drawFromCenter:point |
| 412 radius:0.0 | 383 radius:0.0 |
| 413 toCenter:point | 384 toCenter:point |
| 414 radius:NSWidth(rect) / 3.0 | 385 radius:NSWidth(rect) / 3.0 |
| 415 options:NSGradientDrawsBeforeStartingLocation]; | 386 options:NSGradientDrawsBeforeStartingLocation]; |
| 416 } | 387 } |
| 417 } | 388 } |
| 418 | 389 |
| 419 CGContextEndTransparencyLayer(cgContext); | 390 CGContextEndTransparencyLayer(cgContext); |
| 420 } | 391 } |
| 421 } | 392 } |
| 422 | 393 |
| 423 // Draws the tab outline. | 394 // Draws the tab outline. |
| 424 - (void)drawStroke:(NSRect)dirtyRect { | 395 - (void)drawStroke:(NSRect)dirtyRect { |
| 425 BOOL focused = [[self window] isMainWindow]; | 396 CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha; |
| 426 CGFloat alpha = focused ? 1.0 : tabs::kImageNoFocusAlpha; | 397 GetStrokeImage(state_ == NSOnState) |
| 427 | 398 ->DrawInRect([self bounds], NSCompositeSourceOver, alpha); |
| 428 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 429 float height = | |
| 430 [rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage() size].height; | |
| 431 if (state_ == NSOnState) { | |
| 432 NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height), | |
| 433 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage(), | |
| 434 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_CENTER).ToNSImage(), | |
| 435 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_RIGHT).ToNSImage(), | |
| 436 /*vertical=*/NO, | |
| 437 NSCompositeSourceOver, | |
| 438 alpha, | |
| 439 /*flipped=*/NO); | |
| 440 } else { | |
| 441 NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height), | |
| 442 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_LEFT).ToNSImage(), | |
| 443 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_CENTER).ToNSImage(), | |
| 444 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_RIGHT).ToNSImage(), | |
| 445 /*vertical=*/NO, | |
| 446 NSCompositeSourceOver, | |
| 447 alpha, | |
| 448 /*flipped=*/NO); | |
| 449 } | |
| 450 } | 399 } |
| 451 | 400 |
| 452 - (void)drawRect:(NSRect)dirtyRect { | 401 - (void)drawRect:(NSRect)dirtyRect { |
| 453 // Close button and image are drawn by subviews. | |
| 454 [self drawFill:dirtyRect]; | 402 [self drawFill:dirtyRect]; |
| 455 [self drawStroke:dirtyRect]; | 403 [self drawStroke:dirtyRect]; |
| 456 | 404 |
| 457 // We draw the title string directly instead of using a NSTextField subview. | 405 // We draw the title string directly instead of using a NSTextField subview. |
| 458 // This is so that we can get font smoothing to work on earlier OS, and even | 406 // This is so that we can get font smoothing to work on earlier OS, and even |
| 459 // when the tab background is a pattern image (when using themes). | 407 // when the tab background is a pattern image (when using themes). |
| 460 if (![titleView_ isHidden]) { | 408 if (![titleView_ isHidden]) { |
| 461 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 409 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 462 NSGraphicsContext* context = [NSGraphicsContext currentContext]; | 410 NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
| 463 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); | 411 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 742 } | 690 } |
| 743 } | 691 } |
| 744 | 692 |
| 745 if (nextUpdate < kNoUpdate) | 693 if (nextUpdate < kNoUpdate) |
| 746 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate]; | 694 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate]; |
| 747 | 695 |
| 748 [self resetLastGlowUpdateTime]; | 696 [self resetLastGlowUpdateTime]; |
| 749 [self setNeedsDisplay:YES]; | 697 [self setNeedsDisplay:YES]; |
| 750 } | 698 } |
| 751 | 699 |
| 752 - (CGImageRef)tabClippingMask { | |
| 753 // NOTE: NSHeight([self bounds]) doesn't match the height of the bitmaps. | |
| 754 CGFloat scale = 1; | |
| 755 if ([[self window] respondsToSelector:@selector(backingScaleFactor)]) | |
| 756 scale = [[self window] backingScaleFactor]; | |
| 757 | |
| 758 NSRect bounds = [self bounds]; | |
| 759 CGFloat tabWidth = NSWidth(bounds); | |
| 760 if (tabWidth == maskCacheWidth_ && scale == maskCacheScale_) | |
| 761 return maskCache_.get(); | |
| 762 | |
| 763 maskCacheWidth_ = tabWidth; | |
| 764 maskCacheScale_ = scale; | |
| 765 | |
| 766 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 767 NSImage* leftMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage(); | |
| 768 NSImage* rightMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage(); | |
| 769 | |
| 770 CGFloat leftWidth = leftMask.size.width; | |
| 771 CGFloat rightWidth = rightMask.size.width; | |
| 772 | |
| 773 // Image masks must be in the DeviceGray colorspace. Create a context and | |
| 774 // draw the mask into it. | |
| 775 base::ScopedCFTypeRef<CGColorSpaceRef> colorspace( | |
| 776 CGColorSpaceCreateDeviceGray()); | |
| 777 base::ScopedCFTypeRef<CGContextRef> maskContext( | |
| 778 CGBitmapContextCreate(NULL, tabWidth * scale, kMaskHeight * scale, | |
| 779 8, tabWidth * scale, colorspace, 0)); | |
| 780 CGContextScaleCTM(maskContext, scale, scale); | |
| 781 NSGraphicsContext* maskGraphicsContext = | |
| 782 [NSGraphicsContext graphicsContextWithGraphicsPort:maskContext | |
| 783 flipped:NO]; | |
| 784 | |
| 785 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | |
| 786 [NSGraphicsContext setCurrentContext:maskGraphicsContext]; | |
| 787 | |
| 788 // Draw mask image. | |
| 789 [[NSColor blackColor] setFill]; | |
| 790 CGContextFillRect(maskContext, CGRectMake(0, 0, tabWidth, kMaskHeight)); | |
| 791 | |
| 792 NSDrawThreePartImage(NSMakeRect(0, 0, tabWidth, kMaskHeight), | |
| 793 leftMask, nil, rightMask, /*vertical=*/NO, NSCompositeSourceOver, 1.0, | |
| 794 /*flipped=*/NO); | |
| 795 | |
| 796 CGFloat middleWidth = tabWidth - leftWidth - rightWidth; | |
| 797 NSRect middleRect = NSMakeRect(leftWidth, 0, middleWidth, kFillHeight); | |
| 798 [[NSColor whiteColor] setFill]; | |
| 799 NSRectFill(middleRect); | |
| 800 | |
| 801 maskCache_.reset(CGBitmapContextCreateImage(maskContext)); | |
| 802 return maskCache_; | |
| 803 } | |
| 804 | |
| 805 @end // @implementation TabView(Private) | 700 @end // @implementation TabView(Private) |
| OLD | NEW |