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); | |
Nico
2015/02/13 04:17:27
nit: We have CR_DEFINE_STATIC_LOCAL for this (also
| |
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 |