Chromium Code Reviews| 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/l10n/l10n_util.h" | 22 #include "ui/base/l10n/l10n_util.h" |
| 23 #include "ui/base/resource/resource_bundle.h" | 23 #include "ui/base/resource/resource_bundle.h" |
| 24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | 24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
| 25 | 25 |
| 26 | 26 |
| 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. | 27 const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. |
| 29 | 28 |
| 30 // The amount of time in seconds during which each type of glow increases, holds | 29 // The amount of time in seconds during which each type of glow increases, holds |
| 31 // steady, and decreases, respectively. | 30 // steady, and decreases, respectively. |
| 32 const NSTimeInterval kHoverShowDuration = 0.2; | 31 const NSTimeInterval kHoverShowDuration = 0.2; |
| 33 const NSTimeInterval kHoverHoldDuration = 0.02; | 32 const NSTimeInterval kHoverHoldDuration = 0.02; |
| 34 const NSTimeInterval kHoverHideDuration = 0.4; | 33 const NSTimeInterval kHoverHideDuration = 0.4; |
| 35 const NSTimeInterval kAlertShowDuration = 0.4; | 34 const NSTimeInterval kAlertShowDuration = 0.4; |
| 36 const NSTimeInterval kAlertHoldDuration = 0.4; | 35 const NSTimeInterval kAlertHoldDuration = 0.4; |
| 37 const NSTimeInterval kAlertHideDuration = 0.4; | 36 const NSTimeInterval kAlertHideDuration = 0.4; |
| 38 | 37 |
| 39 // The default time interval in seconds between glow updates (when | 38 // The default time interval in seconds between glow updates (when |
| 40 // increasing/decreasing). | 39 // increasing/decreasing). |
| 41 const NSTimeInterval kGlowUpdateInterval = 0.025; | 40 const NSTimeInterval kGlowUpdateInterval = 0.025; |
| 42 | 41 |
| 43 // This is used to judge whether the mouse has moved during rapid closure; if it | 42 // 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. | 43 // has moved less than the threshold, we want to close the tab. |
| 45 const CGFloat kRapidCloseDist = 2.5; | 44 const CGFloat kRapidCloseDist = 2.5; |
| 46 | 45 |
| 46 namespace { | |
| 47 | |
| 48 // A wrapper around NSDrawThreePartImage that caches the images. | |
| 49 // The middle image is optional. | |
| 50 class ThreePartImage { | |
| 51 public: | |
| 52 ThreePartImage(int left_id, int middle_id, int right_id) { | |
| 53 DCHECK(left_id); | |
| 54 DCHECK(right_id); | |
| 55 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 56 leftImage_.reset(rb.GetNativeImageNamed(left_id).CopyNSImage()); | |
| 57 rightImage_.reset(rb.GetNativeImageNamed(right_id).CopyNSImage()); | |
| 58 leftSize_ = [leftImage_ size]; | |
| 59 rightSize_ = [rightImage_ size]; | |
| 60 | |
| 61 if (middle_id) | |
| 62 middleImage_.reset(rb.GetNativeImageNamed(middle_id).CopyNSImage()); | |
| 63 } | |
| 64 | |
| 65 void DrawInRect(NSRect rect, NSCompositingOperation op, CGFloat alpha) { | |
| 66 rect.size.height = leftSize_.height; | |
| 67 NSDrawThreePartImage(rect, leftImage_, middleImage_, rightImage_, | |
| 68 NO, op, alpha, NO); | |
| 69 } | |
| 70 | |
| 71 NSRect GetLeftRect(NSRect bounds) { | |
| 72 NSRect left, right; | |
| 73 NSDivideRect(bounds, &left, &right, leftSize_.width, NSMinXEdge); | |
| 74 return left; | |
| 75 } | |
| 76 | |
| 77 NSRect GetMiddleRect(NSRect bounds) { | |
| 78 NSRect left, middle, right; | |
| 79 NSDivideRect(bounds, &left, &middle, leftSize_.width, NSMinXEdge); | |
| 80 NSDivideRect(middle, &right, &middle, rightSize_.width, NSMaxXEdge); | |
| 81 return middle; | |
| 82 } | |
| 83 | |
| 84 NSRect GetRightRect(NSRect bounds) { | |
| 85 NSRect left, right; | |
| 86 NSDivideRect(bounds, &right, &left, rightSize_.width, NSMaxXEdge); | |
| 87 return right; | |
| 88 } | |
| 89 | |
| 90 // Returns YES if |point| is in a non-transparent part of the images. | |
| 91 BOOL HitTest(NSPoint point, NSRect bounds) { | |
| 92 NSRect middleRect = GetMiddleRect(bounds); | |
| 93 if (NSPointInRect(point, middleRect)) | |
| 94 return middleImage_ ? HitTestImage(point, middleImage_, middleRect) : YES; | |
| 95 | |
| 96 NSRect leftRect = GetLeftRect(bounds); | |
| 97 if (NSPointInRect(point, leftRect)) | |
| 98 return HitTestImage(point, leftImage_, leftRect); | |
| 99 | |
| 100 NSRect rightRect = GetRightRect(bounds); | |
| 101 if (NSPointInRect(point, rightRect)) | |
| 102 return HitTestImage(point, rightImage_, rightRect); | |
| 103 | |
| 104 return NO; | |
| 105 } | |
| 106 | |
| 107 private: | |
| 108 // Returns YES if |point| is in a non-transparent part of |image|. | |
| 109 BOOL HitTestImage(NSPoint point, NSImage* image, NSRect imageRect) { | |
| 110 NSRect pointRect = NSMakeRect(point.x, point.y, 1, 1); | |
| 111 return [image hitTestRect:pointRect | |
| 112 withImageDestinationRect:imageRect | |
| 113 context:nil | |
| 114 hints:nil | |
| 115 flipped:NO]; | |
| 116 } | |
| 117 | |
| 118 base::scoped_nsobject<NSImage> leftImage_; | |
| 119 base::scoped_nsobject<NSImage> middleImage_; | |
| 120 base::scoped_nsobject<NSImage> rightImage_; | |
| 121 NSSize leftSize_; | |
| 122 NSSize rightSize_; | |
| 123 | |
| 124 DISALLOW_COPY_AND_ASSIGN(ThreePartImage); | |
| 125 }; | |
| 126 | |
| 127 ThreePartImage* GetMaskImage() { | |
| 128 static ThreePartImage* mask = | |
| 129 new ThreePartImage(IDR_TAB_ALPHA_LEFT, 0, IDR_TAB_ALPHA_RIGHT); | |
| 130 return mask; | |
| 131 } | |
| 132 | |
| 133 ThreePartImage* GetStrokeImage(bool active) { | |
| 134 static ThreePartImage* activeStroke = new ThreePartImage( | |
| 135 IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT); | |
| 136 static ThreePartImage* inactiveStroke = new ThreePartImage( | |
| 137 IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT); | |
| 138 | |
| 139 return active ? activeStroke : inactiveStroke; | |
| 140 } | |
| 141 | |
| 142 } // namespace | |
| 143 | |
| 47 @interface TabView(Private) | 144 @interface TabView(Private) |
| 48 | 145 |
| 49 - (void)resetLastGlowUpdateTime; | 146 - (void)resetLastGlowUpdateTime; |
| 50 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate; | 147 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate; |
| 51 - (void)adjustGlowValue; | 148 - (void)adjustGlowValue; |
| 52 - (CGImageRef)tabClippingMask; | |
| 53 | 149 |
| 54 @end // TabView(Private) | 150 @end // TabView(Private) |
| 55 | 151 |
| 56 @implementation TabView | 152 @implementation TabView |
| 57 | 153 |
| 58 @synthesize state = state_; | 154 @synthesize state = state_; |
| 59 @synthesize hoverAlpha = hoverAlpha_; | 155 @synthesize hoverAlpha = hoverAlpha_; |
| 60 @synthesize alertAlpha = alertAlpha_; | 156 @synthesize alertAlpha = alertAlpha_; |
| 61 @synthesize closing = closing_; | 157 @synthesize closing = closing_; |
| 62 | 158 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 75 [titleView_ setAutoresizingMask:NSViewWidthSizable]; | 171 [titleView_ setAutoresizingMask:NSViewWidthSizable]; |
| 76 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( | 172 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( |
| 77 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); | 173 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); |
| 78 [labelCell setControlSize:NSSmallControlSize]; | 174 [labelCell setControlSize:NSSmallControlSize]; |
| 79 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; | 175 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; |
| 80 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName] | 176 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName] |
| 81 size:fontSize]; | 177 size:fontSize]; |
| 82 [labelCell setFont:font]; | 178 [labelCell setFont:font]; |
| 83 [titleView_ setCell:labelCell]; | 179 [titleView_ setCell:labelCell]; |
| 84 titleViewCell_ = labelCell; | 180 titleViewCell_ = labelCell; |
| 181 | |
| 182 [self setWantsLayer:YES]; // -drawFill: needs a layer. | |
| 85 } | 183 } |
| 86 return self; | 184 return self; |
| 87 } | 185 } |
| 88 | 186 |
| 89 - (void)dealloc { | 187 - (void)dealloc { |
| 90 // Cancel any delayed requests that may still be pending (drags or hover). | 188 // Cancel any delayed requests that may still be pending (drags or hover). |
| 91 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 189 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 92 [super dealloc]; | 190 [super dealloc]; |
| 93 } | 191 } |
| 94 | 192 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 } | 248 } |
| 151 | 249 |
| 152 // Determines which view a click in our frame actually hit. It's either this | 250 // Determines which view a click in our frame actually hit. It's either this |
| 153 // view or one of the child buttons. | 251 // view or one of the child buttons. |
| 154 - (NSView*)hitTest:(NSPoint)aPoint { | 252 - (NSView*)hitTest:(NSPoint)aPoint { |
| 155 NSView* const defaultHitTestResult = [super hitTest:aPoint]; | 253 NSView* const defaultHitTestResult = [super hitTest:aPoint]; |
| 156 if ([defaultHitTestResult isKindOfClass:[NSButton class]]) | 254 if ([defaultHitTestResult isKindOfClass:[NSButton class]]) |
| 157 return defaultHitTestResult; | 255 return defaultHitTestResult; |
| 158 | 256 |
| 159 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; | 257 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; |
| 160 NSRect pointRect = NSMakeRect(viewPoint.x, viewPoint.y, 1, 1); | 258 NSRect maskRect = [self bounds]; |
| 161 | 259 maskRect.size.height = kFillHeight; |
| 162 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 260 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 } | 261 } |
| 189 | 262 |
| 190 // Returns |YES| if this tab can be torn away into a new window. | 263 // Returns |YES| if this tab can be torn away into a new window. |
| 191 - (BOOL)canBeDragged { | 264 - (BOOL)canBeDragged { |
| 192 return [controller_ tabCanBeDragged:controller_]; | 265 return [controller_ tabCanBeDragged:controller_]; |
| 193 } | 266 } |
| 194 | 267 |
| 195 // Handle clicks and drags in this button. We get here because we have | 268 // Handle clicks and drags in this button. We get here because we have |
| 196 // overridden acceptsFirstMouse: and the click is within our bounds. | 269 // overridden acceptsFirstMouse: and the click is within our bounds. |
| 197 - (void)mouseDown:(NSEvent*)theEvent { | 270 - (void)mouseDown:(NSEvent*)theEvent { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 }, | 378 }, |
| 306 }; | 379 }; |
| 307 | 380 |
| 308 // Themes don't have an inactive image so only look for one if there's no | 381 // Themes don't have an inactive image so only look for one if there's no |
| 309 // theme. | 382 // theme. |
| 310 bool active = | 383 bool active = |
| 311 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme(); | 384 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme(); |
| 312 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); | 385 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); |
| 313 } | 386 } |
| 314 | 387 |
| 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. | 388 // Draws the tab background. |
| 331 - (void)drawFill:(NSRect)dirtyRect { | 389 - (void)drawFill:(NSRect)dirtyRect { |
| 332 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 390 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 391 NSRect bounds = [self bounds]; | |
| 392 | |
| 393 NSRect clippingRect = bounds; | |
| 394 clippingRect.size.height = kFillHeight; | |
| 395 if (state_ != NSOnState) { | |
| 396 // Background tabs should not paint over the tab strip separator, which is | |
| 397 // two pixels high in both lodpi and hidpi. | |
| 398 clippingRect.origin.y = 2 * [self cr_lineWidth]; | |
| 399 clippingRect.size.height -= clippingRect.origin.y; | |
| 400 } | |
| 401 NSRectClip(clippingRect); | |
| 402 | |
| 403 NSPoint position = [[self window] | |
| 404 themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP]; | |
| 405 [[NSGraphicsContext currentContext] cr_setPatternPhase:position forView:self]; | |
| 406 | |
| 407 [[self backgroundColorForSelected:(state_ != NSOffState)] set]; | |
| 408 NSRectFill(dirtyRect); | |
| 409 | |
| 410 if (state_ == NSOffState) | |
| 411 [self drawGlow:dirtyRect]; | |
| 412 | |
| 413 // If we filled outside the middle rect, we need to erase what we filled | |
| 414 // outside the tab's shape. | |
| 415 // This only works if we are drawing to our own backing layer. | |
| 416 if (!NSContainsRect(GetMaskImage()->GetMiddleRect(bounds), dirtyRect)) { | |
| 417 DCHECK([self layer]); | |
| 418 GetMaskImage()->DrawInRect(bounds, NSCompositeDestinationIn, 1.0); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 // Draw the glow for hover and the overlay for alerts. | |
| 423 - (void)drawGlow:(NSRect)dirtyRect { | |
| 333 NSGraphicsContext* context = [NSGraphicsContext currentContext]; | 424 NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
| 334 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); | 425 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
| 335 | 426 |
| 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]; | 427 CGFloat hoverAlpha = [self hoverAlpha]; |
| 380 CGFloat alertAlpha = [self alertAlpha]; | 428 CGFloat alertAlpha = [self alertAlpha]; |
| 381 if (hoverAlpha > 0 || alertAlpha > 0) { | 429 if (hoverAlpha > 0 || alertAlpha > 0) { |
| 382 gfx::ScopedNSGraphicsContextSaveGState contextSave; | |
|
Robert Sesek
2015/02/12 17:16:47
Why remove this line?
Andre
2015/02/12 17:23:01
It seems unnecessary.
The documentation for CGCont
| |
| 383 CGContextBeginTransparencyLayer(cgContext, 0); | 430 CGContextBeginTransparencyLayer(cgContext, 0); |
| 384 | 431 |
| 385 // The alert glow overlay is like the selected state but at most at most 80% | 432 // 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%. | 433 // opaque. The hover glow brings up the overlay's opacity at most 50%. |
| 387 CGFloat backgroundAlpha = 0.8 * alertAlpha; | 434 CGFloat backgroundAlpha = 0.8 * alertAlpha; |
| 388 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; | 435 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; |
| 389 CGContextSetAlpha(cgContext, backgroundAlpha); | 436 CGContextSetAlpha(cgContext, backgroundAlpha); |
| 390 | 437 |
| 391 [self drawFillForActiveTab:dirtyRect]; | 438 [[self backgroundColorForSelected:YES] set]; |
| 439 NSRectFill(dirtyRect); | |
| 392 | 440 |
| 393 // ui::ThemeProvider::HasCustomImage is true only if the theme provides the | 441 // 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 | 442 // 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 | 443 // theme machinery will make one if given a frame image. See |
| 396 // BrowserThemePack::GenerateTabBackgroundImages for details. | 444 // BrowserThemePack::GenerateTabBackgroundImages for details. |
| 445 ui::ThemeProvider* themeProvider = [[self window] themeProvider]; | |
| 397 BOOL hasCustomTheme = themeProvider && | 446 BOOL hasCustomTheme = themeProvider && |
| 398 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || | 447 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || |
| 399 themeProvider->HasCustomImage(IDR_THEME_FRAME)); | 448 themeProvider->HasCustomImage(IDR_THEME_FRAME)); |
| 400 // Draw a mouse hover gradient for the default themes. | 449 // Draw a mouse hover gradient for the default themes. |
| 401 if (hoverAlpha > 0) { | 450 if (hoverAlpha > 0) { |
| 402 if (themeProvider && !hasCustomTheme) { | 451 if (themeProvider && !hasCustomTheme) { |
| 403 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); | 452 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); |
| 404 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 | 453 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 |
| 405 alpha:1.0 * hoverAlpha] | 454 alpha:1.0 * hoverAlpha] |
| 406 endingColor:[NSColor colorWithCalibratedWhite:1.0 | 455 endingColor:[NSColor colorWithCalibratedWhite:1.0 |
| 407 alpha:0.0]]; | 456 alpha:0.0]]; |
| 408 NSRect rect = [self bounds]; | 457 NSRect rect = [self bounds]; |
| 409 NSPoint point = hoverPoint_; | 458 NSPoint point = hoverPoint_; |
| 410 point.y = NSHeight(rect); | 459 point.y = NSHeight(rect); |
| 411 [glow drawFromCenter:point | 460 [glow drawFromCenter:point |
| 412 radius:0.0 | 461 radius:0.0 |
| 413 toCenter:point | 462 toCenter:point |
| 414 radius:NSWidth(rect) / 3.0 | 463 radius:NSWidth(rect) / 3.0 |
| 415 options:NSGradientDrawsBeforeStartingLocation]; | 464 options:NSGradientDrawsBeforeStartingLocation]; |
| 416 } | 465 } |
| 417 } | 466 } |
| 418 | 467 |
| 419 CGContextEndTransparencyLayer(cgContext); | 468 CGContextEndTransparencyLayer(cgContext); |
| 420 } | 469 } |
| 421 } | 470 } |
| 422 | 471 |
| 423 // Draws the tab outline. | 472 // Draws the tab outline. |
| 424 - (void)drawStroke:(NSRect)dirtyRect { | 473 - (void)drawStroke:(NSRect)dirtyRect { |
| 425 BOOL focused = [[self window] isMainWindow]; | 474 CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha; |
| 426 CGFloat alpha = focused ? 1.0 : tabs::kImageNoFocusAlpha; | 475 GetStrokeImage(state_ == NSOnState) |
| 427 | 476 ->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 } | 477 } |
| 451 | 478 |
| 452 - (void)drawRect:(NSRect)dirtyRect { | 479 - (void)drawRect:(NSRect)dirtyRect { |
| 453 // Close button and image are drawn by subviews. | |
| 454 [self drawFill:dirtyRect]; | 480 [self drawFill:dirtyRect]; |
| 455 [self drawStroke:dirtyRect]; | 481 [self drawStroke:dirtyRect]; |
| 456 | 482 |
| 457 // We draw the title string directly instead of using a NSTextField subview. | 483 // 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 | 484 // 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). | 485 // when the tab background is a pattern image (when using themes). |
| 460 if (![titleView_ isHidden]) { | 486 if (![titleView_ isHidden]) { |
| 461 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 487 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 462 NSGraphicsContext* context = [NSGraphicsContext currentContext]; | 488 NSGraphicsContext* context = [NSGraphicsContext currentContext]; |
| 463 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); | 489 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 486 if ([self window]) { | 512 if ([self window]) { |
| 487 [controller_ updateTitleColor]; | 513 [controller_ updateTitleColor]; |
| 488 } | 514 } |
| 489 } | 515 } |
| 490 | 516 |
| 491 - (NSString*)title { | 517 - (NSString*)title { |
| 492 return [titleView_ stringValue]; | 518 return [titleView_ stringValue]; |
| 493 } | 519 } |
| 494 | 520 |
| 495 - (void)setTitle:(NSString*)title { | 521 - (void)setTitle:(NSString*)title { |
| 522 if ([title isEqualToString:[titleView_ stringValue]]) | |
| 523 return; | |
| 524 | |
| 496 [titleView_ setStringValue:title]; | 525 [titleView_ setStringValue:title]; |
| 497 | 526 |
| 498 base::string16 title16 = base::SysNSStringToUTF16(title); | 527 base::string16 title16 = base::SysNSStringToUTF16(title); |
| 499 bool isRTL = base::i18n::GetFirstStrongCharacterDirection(title16) == | 528 bool isRTL = base::i18n::GetFirstStrongCharacterDirection(title16) == |
| 500 base::i18n::RIGHT_TO_LEFT; | 529 base::i18n::RIGHT_TO_LEFT; |
| 501 titleViewCell_.truncateMode = isRTL ? GTMFadeTruncatingHead | 530 titleViewCell_.truncateMode = isRTL ? GTMFadeTruncatingHead |
| 502 : GTMFadeTruncatingTail; | 531 : GTMFadeTruncatingTail; |
| 503 | 532 |
| 504 [self setNeedsDisplayInRect:[titleView_ frame]]; | 533 [self setNeedsDisplayInRect:[titleView_ frame]]; |
| 505 } | 534 } |
| 506 | 535 |
| 507 - (NSRect)titleFrame { | 536 - (NSRect)titleFrame { |
| 508 return [titleView_ frame]; | 537 return [titleView_ frame]; |
| 509 } | 538 } |
| 510 | 539 |
| 511 - (void)setTitleFrame:(NSRect)titleFrame { | 540 - (void)setTitleFrame:(NSRect)titleFrame { |
| 541 NSRect oldTitleFrame = [titleView_ frame]; | |
| 542 if (NSEqualRects(titleFrame, oldTitleFrame)) | |
| 543 return; | |
| 512 [titleView_ setFrame:titleFrame]; | 544 [titleView_ setFrame:titleFrame]; |
| 513 [self setNeedsDisplayInRect:titleFrame]; | 545 [self setNeedsDisplayInRect:NSUnionRect(titleFrame, oldTitleFrame)]; |
| 514 } | 546 } |
| 515 | 547 |
| 516 - (NSColor*)titleColor { | 548 - (NSColor*)titleColor { |
| 517 return [titleView_ textColor]; | 549 return [titleView_ textColor]; |
| 518 } | 550 } |
| 519 | 551 |
| 520 - (void)setTitleColor:(NSColor*)titleColor { | 552 - (void)setTitleColor:(NSColor*)titleColor { |
| 553 if ([titleColor isEqual:[titleView_ textColor]]) | |
| 554 return; | |
| 521 [titleView_ setTextColor:titleColor]; | 555 [titleView_ setTextColor:titleColor]; |
| 522 [self setNeedsDisplayInRect:[titleView_ frame]]; | 556 [self setNeedsDisplayInRect:[titleView_ frame]]; |
| 523 } | 557 } |
| 524 | 558 |
| 525 - (BOOL)titleHidden { | 559 - (BOOL)titleHidden { |
| 526 return [titleView_ isHidden]; | 560 return [titleView_ isHidden]; |
| 527 } | 561 } |
| 528 | 562 |
| 529 - (void)setTitleHidden:(BOOL)titleHidden { | 563 - (void)setTitleHidden:(BOOL)titleHidden { |
| 564 if (titleHidden == [titleView_ isHidden]) | |
| 565 return; | |
| 530 [titleView_ setHidden:titleHidden]; | 566 [titleView_ setHidden:titleHidden]; |
| 531 [self setNeedsDisplayInRect:[titleView_ frame]]; | 567 [self setNeedsDisplayInRect:[titleView_ frame]]; |
| 532 } | 568 } |
| 533 | 569 |
| 534 - (void)setState:(NSCellStateValue)state { | 570 - (void)setState:(NSCellStateValue)state { |
| 535 if (state_ == state) | 571 if (state_ == state) |
| 536 return; | 572 return; |
| 537 state_ = state; | 573 state_ = state; |
| 538 [self setNeedsDisplay:YES]; | 574 [self setNeedsDisplay:YES]; |
| 539 } | 575 } |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 732 } | 768 } |
| 733 } | 769 } |
| 734 | 770 |
| 735 if (nextUpdate < kNoUpdate) | 771 if (nextUpdate < kNoUpdate) |
| 736 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate]; | 772 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate]; |
| 737 | 773 |
| 738 [self resetLastGlowUpdateTime]; | 774 [self resetLastGlowUpdateTime]; |
| 739 [self setNeedsDisplay:YES]; | 775 [self setNeedsDisplay:YES]; |
| 740 } | 776 } |
| 741 | 777 |
| 742 - (CGImageRef)tabClippingMask { | |
| 743 // NOTE: NSHeight([self bounds]) doesn't match the height of the bitmaps. | |
| 744 CGFloat scale = 1; | |
| 745 if ([[self window] respondsToSelector:@selector(backingScaleFactor)]) | |
| 746 scale = [[self window] backingScaleFactor]; | |
| 747 | |
| 748 NSRect bounds = [self bounds]; | |
| 749 CGFloat tabWidth = NSWidth(bounds); | |
| 750 if (tabWidth == maskCacheWidth_ && scale == maskCacheScale_) | |
| 751 return maskCache_.get(); | |
| 752 | |
| 753 maskCacheWidth_ = tabWidth; | |
| 754 maskCacheScale_ = scale; | |
| 755 | |
| 756 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 757 NSImage* leftMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage(); | |
| 758 NSImage* rightMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage(); | |
| 759 | |
| 760 CGFloat leftWidth = leftMask.size.width; | |
| 761 CGFloat rightWidth = rightMask.size.width; | |
| 762 | |
| 763 // Image masks must be in the DeviceGray colorspace. Create a context and | |
| 764 // draw the mask into it. | |
| 765 base::ScopedCFTypeRef<CGColorSpaceRef> colorspace( | |
| 766 CGColorSpaceCreateDeviceGray()); | |
| 767 base::ScopedCFTypeRef<CGContextRef> maskContext( | |
| 768 CGBitmapContextCreate(NULL, tabWidth * scale, kMaskHeight * scale, | |
| 769 8, tabWidth * scale, colorspace, 0)); | |
| 770 CGContextScaleCTM(maskContext, scale, scale); | |
| 771 NSGraphicsContext* maskGraphicsContext = | |
| 772 [NSGraphicsContext graphicsContextWithGraphicsPort:maskContext | |
| 773 flipped:NO]; | |
| 774 | |
| 775 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | |
| 776 [NSGraphicsContext setCurrentContext:maskGraphicsContext]; | |
| 777 | |
| 778 // Draw mask image. | |
| 779 [[NSColor blackColor] setFill]; | |
| 780 CGContextFillRect(maskContext, CGRectMake(0, 0, tabWidth, kMaskHeight)); | |
| 781 | |
| 782 NSDrawThreePartImage(NSMakeRect(0, 0, tabWidth, kMaskHeight), | |
| 783 leftMask, nil, rightMask, /*vertical=*/NO, NSCompositeSourceOver, 1.0, | |
| 784 /*flipped=*/NO); | |
| 785 | |
| 786 CGFloat middleWidth = tabWidth - leftWidth - rightWidth; | |
| 787 NSRect middleRect = NSMakeRect(leftWidth, 0, middleWidth, kFillHeight); | |
| 788 [[NSColor whiteColor] setFill]; | |
| 789 NSRectFill(middleRect); | |
| 790 | |
| 791 maskCache_.reset(CGBitmapContextCreateImage(maskContext)); | |
| 792 return maskCache_; | |
| 793 } | |
| 794 | |
| 795 @end // @implementation TabView(Private) | 778 @end // @implementation TabView(Private) |
| OLD | NEW |