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 |