Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(647)

Side by Side Diff: chrome/browser/ui/cocoa/tabs/tab_view.mm

Issue 918533005: Mac: Optimize TabView drawing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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)
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/tabs/tab_view.h ('k') | chrome/browser/ui/cocoa/tabs/tab_view_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698