OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 #include "chrome/browser/cocoa/nsimage_cache.h" | 5 #include "chrome/browser/cocoa/nsimage_cache.h" |
6 #include "chrome/browser/cocoa/tab_controller.h" | 6 #include "chrome/browser/cocoa/tab_controller.h" |
7 #include "chrome/browser/cocoa/tab_view.h" | 7 #include "chrome/browser/cocoa/tab_view.h" |
8 #include "chrome/browser/cocoa/tab_window_controller.h" | 8 #include "chrome/browser/cocoa/tab_window_controller.h" |
9 | 9 |
10 | 10 |
11 // Constants for inset and control points for tab shape. | 11 // Constants for inset and control points for tab shape. |
12 static const CGFloat kInsetMultiplier = 2.0/3.0; | 12 static const CGFloat kInsetMultiplier = 2.0/3.0; |
13 static const CGFloat kControlPoint1Multiplier = 1.0/3.0; | 13 static const CGFloat kControlPoint1Multiplier = 1.0/3.0; |
14 static const CGFloat kControlPoint2Multiplier = 3.0/8.0; | 14 static const CGFloat kControlPoint2Multiplier = 3.0/8.0; |
15 | 15 |
16 static const NSTimeInterval kAnimationShowDuration = 0.2; | |
17 static const NSTimeInterval kAnimationHideDuration = 0.4; | |
18 | |
19 @implementation TabView | 16 @implementation TabView |
20 | 17 |
21 @synthesize state = state_; | 18 @synthesize state = state_; |
22 @synthesize hoverAlpha = hoverAlpha_; | |
23 | 19 |
24 - (id)initWithFrame:(NSRect)frame { | 20 - (id)initWithFrame:(NSRect)frame { |
25 self = [super initWithFrame:frame]; | 21 self = [super initWithFrame:frame]; |
26 if (self) { | 22 if (self) { |
27 chromeIsVisible_ = YES; | 23 chromeIsVisible_ = YES; |
28 [self setShowsDivider:NO]; | 24 [self setShowsDivider:NO]; |
29 // TODO(alcor): register for theming, either here or the cell | 25 // TODO(alcor): register for theming, either here or the cell |
30 // [self gtm_registerForThemeNotifications]; | 26 // [self gtm_registerForThemeNotifications]; |
31 } | 27 } |
32 return self; | 28 return self; |
33 } | 29 } |
34 | 30 |
35 - (void)awakeFromNib { | 31 - (void)awakeFromNib { |
36 [self setShowsDivider:NO]; | 32 [self setShowsDivider:NO]; |
37 // Set up the tracking rect for the close button mouseover. Add it | 33 // Set up the tracking rect for the close button mouseover. Add it |
38 // to the |closeButton_| view, but we'll handle the message ourself. | 34 // to the |closeButton_| view, but we'll handle the message ourself. |
39 // The mouseover is always enabled, because the close button works | 35 // The mouseover is always enabled, because the close button works |
40 // regardless of key/main/active status. | 36 // regardless of key/main/active status. |
41 closeTrackingArea_.reset( | 37 trackingArea_.reset( |
42 [[NSTrackingArea alloc] initWithRect:[closeButton_ bounds] | 38 [[NSTrackingArea alloc] initWithRect:[closeButton_ bounds] |
43 options:NSTrackingMouseEnteredAndExited | | 39 options:NSTrackingMouseEnteredAndExited | |
44 NSTrackingActiveAlways | 40 NSTrackingActiveAlways |
45 owner:self | 41 owner:self |
46 userInfo:nil]); | 42 userInfo:nil]); |
47 [closeButton_ addTrackingArea:closeTrackingArea_.get()]; | 43 [closeButton_ addTrackingArea:trackingArea_.get()]; |
48 } | 44 } |
49 | 45 |
50 - (void)dealloc { | 46 - (void)dealloc { |
51 // [self gtm_unregisterForThemeNotifications]; | 47 // [self gtm_unregisterForThemeNotifications]; |
52 [closeButton_ removeTrackingArea:closeTrackingArea_.get()]; | 48 [closeButton_ removeTrackingArea:trackingArea_.get()]; |
53 [super dealloc]; | 49 [super dealloc]; |
54 } | 50 } |
55 | 51 |
56 // Overridden so that mouse clicks come to this view (the parent of the | 52 // Overridden so that mouse clicks come to this view (the parent of the |
57 // hierarchy) first. We want to handle clicks and drags in this class and | 53 // hierarchy) first. We want to handle clicks and drags in this class and |
58 // leave the background button for display purposes only. | 54 // leave the background button for display purposes only. |
59 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { | 55 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { |
60 return YES; | 56 return YES; |
61 } | 57 } |
62 | 58 |
63 - (void)adjustHoverValue { | |
64 NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; | |
65 | |
66 NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; | |
67 | |
68 CGFloat opacity = [self hoverAlpha]; | |
69 if (isMouseInside_) { | |
70 opacity += elapsed / kAnimationShowDuration; | |
71 } else { | |
72 opacity -= elapsed / kAnimationHideDuration; | |
73 } | |
74 | |
75 if (!isMouseInside_ && opacity < 0) { | |
76 opacity = 0; | |
77 } else if (isMouseInside_ && opacity > 1) { | |
78 opacity = 1; | |
79 } else { | |
80 [self performSelector:_cmd withObject:nil afterDelay:0.02]; | |
81 } | |
82 lastHoverUpdate_ = thisUpdate; | |
83 [self setHoverAlpha:opacity]; | |
84 | |
85 [self setNeedsDisplay:YES]; | |
86 } | |
87 | |
88 - (void)mouseEntered:(NSEvent *)theEvent { | 59 - (void)mouseEntered:(NSEvent *)theEvent { |
89 if ([theEvent trackingArea] == closeTrackingArea_) { | 60 // We only set up one tracking area, so we know any mouseEntered: |
90 [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar_h.pdf")]; | 61 // messages are for close button mouseovers. |
91 } else { | 62 [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar_h.pdf")]; |
92 lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; | |
93 isMouseInside_ = YES; | |
94 [self adjustHoverValue]; | |
95 [self setNeedsDisplay:YES]; | |
96 } | |
97 } | |
98 | |
99 - (void)mouseMoved:(NSEvent *)theEvent { | |
100 hoverPoint_ = [self convertPoint:[theEvent locationInWindow] | |
101 fromView:nil]; | |
102 [self setNeedsDisplay:YES]; | |
103 } | 63 } |
104 | 64 |
105 - (void)mouseExited:(NSEvent *)theEvent { | 65 - (void)mouseExited:(NSEvent *)theEvent { |
106 if ([theEvent trackingArea] == closeTrackingArea_) { | 66 // We only set up one tracking area, so we know any mouseExited: |
107 [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar.pdf")]; | 67 // messages are for close button mouseovers. |
108 } else { | 68 [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar.pdf")]; |
109 lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; | |
110 isMouseInside_ = NO; | |
111 [self adjustHoverValue]; | |
112 [self setNeedsDisplay:YES]; | |
113 } | |
114 } | 69 } |
115 | 70 |
116 // Determines which view a click in our frame actually hit. It's either this | 71 // Determines which view a click in our frame actually hit. It's either this |
117 // view or our child close button. | 72 // view or our child close button. |
118 - (NSView *)hitTest:(NSPoint)aPoint { | 73 - (NSView *)hitTest:(NSPoint)aPoint { |
119 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; | 74 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; |
120 NSRect frame = [self frame]; | 75 NSRect frame = [self frame]; |
121 | 76 |
122 // Reduce the width of the hit rect slightly to remove the overlap | 77 // Reduce the width of the hit rect slightly to remove the overlap |
123 // between adjacent tabs. The drawing code in TabCell has the top | 78 // between adjacent tabs. The drawing code in TabCell has the top |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
269 } | 224 } |
270 } | 225 } |
271 | 226 |
272 // Iterate over possible targets checking for the one the mouse is in. | 227 // Iterate over possible targets checking for the one the mouse is in. |
273 // The mouse can be in either the tab or window frame. | 228 // The mouse can be in either the tab or window frame. |
274 TabWindowController* newTarget = nil; | 229 TabWindowController* newTarget = nil; |
275 for (TabWindowController* target in targets) { | 230 for (TabWindowController* target in targets) { |
276 NSRect windowFrame = [[target window] frame]; | 231 NSRect windowFrame = [[target window] frame]; |
277 if (NSPointInRect(thisPoint, windowFrame)) { | 232 if (NSPointInRect(thisPoint, windowFrame)) { |
278 NSRect tabStripFrame = [[target tabStripView] frame]; | 233 NSRect tabStripFrame = [[target tabStripView] frame]; |
| 234 tabStripFrame = [[target tabStripView] convertRectToBase:tabStripFrame]; |
279 tabStripFrame.origin = [[target window] | 235 tabStripFrame.origin = [[target window] |
280 convertBaseToScreen:tabStripFrame.origin]; | 236 convertBaseToScreen:tabStripFrame.origin]; |
281 if (NSPointInRect(thisPoint, tabStripFrame)) { | 237 if (NSPointInRect(thisPoint, tabStripFrame)) { |
282 newTarget = target; | 238 newTarget = target; |
283 } | 239 } |
284 break; | 240 break; |
285 } | 241 } |
286 } | 242 } |
287 | 243 |
288 // If we're now targeting a new window, re-layout the tabs in the old | 244 // If we're now targeting a new window, re-layout the tabs in the old |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
463 | 419 |
464 - (void)drawRect:(NSRect)rect { | 420 - (void)drawRect:(NSRect)rect { |
465 [[NSGraphicsContext currentContext] saveGraphicsState]; | 421 [[NSGraphicsContext currentContext] saveGraphicsState]; |
466 rect = [self bounds]; | 422 rect = [self bounds]; |
467 BOOL active = [[self window] isKeyWindow] || [[self window] isMainWindow]; | 423 BOOL active = [[self window] isKeyWindow] || [[self window] isMainWindow]; |
468 BOOL selected = [(NSButton *)self state]; | 424 BOOL selected = [(NSButton *)self state]; |
469 | 425 |
470 // Inset by 0.5 in order to draw on pixels rather than on borders (which would | 426 // Inset by 0.5 in order to draw on pixels rather than on borders (which would |
471 // cause blurry pixels). Decrease height by 1 in order to move away from the | 427 // cause blurry pixels). Decrease height by 1 in order to move away from the |
472 // edge for the dark shadow. | 428 // edge for the dark shadow. |
473 rect = NSInsetRect(rect, -0.5, -0.5); | 429 rect = NSInsetRect(rect, 0.5, -0.5); |
474 rect.origin.y -= 1; | 430 rect.origin.y -= 1; |
475 | 431 |
476 NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect) + 2); | 432 NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect)); |
477 NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect) + 2); | 433 NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect)); |
478 NSPoint topRight = | 434 NSPoint topRight = |
479 NSMakePoint(NSMaxX(rect) - kInsetMultiplier * NSHeight(rect), | 435 NSMakePoint(NSMaxX(rect) - kInsetMultiplier * NSHeight(rect), |
480 NSMaxY(rect)); | 436 NSMaxY(rect)); |
481 NSPoint topLeft = | 437 NSPoint topLeft = |
482 NSMakePoint(NSMinX(rect) + kInsetMultiplier * NSHeight(rect), | 438 NSMakePoint(NSMinX(rect) + kInsetMultiplier * NSHeight(rect), |
483 NSMaxY(rect)); | 439 NSMaxY(rect)); |
484 | 440 |
485 float baseControlPointOutset = NSHeight(rect) * kControlPoint1Multiplier; | 441 float baseControlPointOutset = NSHeight(rect) * kControlPoint1Multiplier; |
486 float bottomControlPointInset = NSHeight(rect) * kControlPoint2Multiplier; | 442 float bottomControlPointInset = NSHeight(rect) * kControlPoint2Multiplier; |
487 | 443 |
488 // Outset many of these values by 1 to cause the fill to bleed outside the | 444 // Outset many of these values by 1 to cause the fill to bleed outside the |
489 // clip area. | 445 // clip area. |
490 NSBezierPath *path = [NSBezierPath bezierPath]; | 446 NSBezierPath *path = [NSBezierPath bezierPath]; |
491 [path moveToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y - 2)]; | 447 [path moveToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y + 1)]; |
492 [path lineToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y)]; | 448 [path lineToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y)]; |
493 [path lineToPoint:bottomLeft]; | 449 [path lineToPoint:bottomLeft]; |
494 [path curveToPoint:topLeft | 450 [path curveToPoint:topLeft |
495 controlPoint1:NSMakePoint(bottomLeft.x + baseControlPointOutset, | 451 controlPoint1:NSMakePoint(bottomLeft.x + baseControlPointOutset, |
496 bottomLeft.y) | 452 bottomLeft.y) |
497 controlPoint2:NSMakePoint(topLeft.x - bottomControlPointInset, | 453 controlPoint2:NSMakePoint(topLeft.x - bottomControlPointInset, |
498 topLeft.y)]; | 454 topLeft.y)]; |
499 [path lineToPoint:topRight]; | 455 [path lineToPoint:topRight]; |
500 [path curveToPoint:bottomRight | 456 [path curveToPoint:bottomRight |
501 controlPoint1:NSMakePoint(topRight.x + bottomControlPointInset, | 457 controlPoint1:NSMakePoint(topRight.x + bottomControlPointInset, |
502 topRight.y) | 458 topRight.y) |
503 controlPoint2:NSMakePoint(bottomRight.x - baseControlPointOutset, | 459 controlPoint2:NSMakePoint(bottomRight.x - baseControlPointOutset, |
504 bottomRight.y)]; | 460 bottomRight.y)]; |
505 [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y)]; | 461 [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y)]; |
506 [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y - 2)]; | 462 [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y + 1)]; |
| 463 |
| 464 if (selected) { |
| 465 // Stroke with a translucent black. |
| 466 [[NSColor colorWithCalibratedWhite:0.0 alpha:active ? 0.5 : 0.3] set]; |
| 467 [[NSGraphicsContext currentContext] saveGraphicsState]; |
| 468 scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); |
| 469 [shadow setShadowOffset:NSMakeSize(2, -1)]; |
| 470 [shadow setShadowBlurRadius:2.0]; |
| 471 [path fill]; |
| 472 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
| 473 } else { |
| 474 // Stroke with a translucent black. |
| 475 [[NSBezierPath bezierPathWithRect:NSOffsetRect([self bounds], 0, 1)] |
| 476 addClip]; |
| 477 |
| 478 [[NSColor colorWithCalibratedWhite:0.0 alpha:active ? 0.3 : 0.1] set]; |
| 479 } |
| 480 |
| 481 [[NSGraphicsContext currentContext] saveGraphicsState]; |
| 482 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; |
| 483 [path setLineWidth:selected ? 2.0 : 1.0]; |
| 484 [path stroke]; |
| 485 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
507 | 486 |
508 GTMTheme *theme = [self gtm_theme]; | 487 GTMTheme *theme = [self gtm_theme]; |
509 | 488 |
510 if (!selected) { | 489 if (!selected) { |
511 NSColor *windowColor = | 490 NSColor *windowColor = |
512 [theme backgroundPatternColorForStyle:GTMThemeStyleWindow | 491 [theme backgroundPatternColorForStyle:GTMThemeStyleWindow |
513 state:GTMThemeStateActiveWindow]; | 492 state:GTMThemeStateActiveWindow]; |
514 if (windowColor) { | 493 if (windowColor) { |
| 494 NSPoint phase = [self patternPhase]; |
515 [windowColor set]; | 495 [windowColor set]; |
516 | 496 [[NSGraphicsContext currentContext] setPatternPhase:phase]; |
517 [[NSGraphicsContext currentContext] setPatternPhase:[self patternPhase]]; | |
518 } else { | 497 } else { |
519 NSPoint phase = [self patternPhase]; | 498 [[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] set]; |
520 phase.y += 1; | |
521 [[NSGraphicsContext currentContext] setPatternPhase:phase]; | |
522 [[NSColor windowBackgroundColor] set]; | |
523 } | |
524 | |
525 [path fill]; | |
526 | |
527 NSColor *tabColor = | |
528 [theme backgroundPatternColorForStyle:GTMThemeStyleTabBarDeselected | |
529 state:GTMThemeStateActiveWindow]; | |
530 if (tabColor) { | |
531 [tabColor set]; | |
532 [[NSGraphicsContext currentContext] setPatternPhase:[self patternPhase]]; | |
533 } else { | |
534 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.3] set]; | |
535 } | 499 } |
536 [path fill]; | 500 [path fill]; |
537 | |
538 } | 501 } |
539 | 502 |
| 503 // Draw the background. |
540 [[NSGraphicsContext currentContext] saveGraphicsState]; | 504 [[NSGraphicsContext currentContext] saveGraphicsState]; |
| 505 CGContextRef context = |
| 506 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); |
| 507 CGContextBeginTransparencyLayer(context, 0); |
| 508 if (!selected) |
| 509 CGContextSetAlpha(context, 0.5); |
541 [path addClip]; | 510 [path addClip]; |
| 511 [super drawRect:rect]; |
542 | 512 |
543 if (selected || hoverAlpha_ > 0) { | 513 CGContextEndTransparencyLayer(context); |
544 // Draw the background. | |
545 CGFloat backgroundAlpha = hoverAlpha_ * 0.5; | |
546 [[NSGraphicsContext currentContext] saveGraphicsState]; | |
547 CGContextRef context = | |
548 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); | |
549 CGContextBeginTransparencyLayer(context, 0); | |
550 if (!selected) | |
551 CGContextSetAlpha(context, backgroundAlpha); | |
552 [path addClip]; | |
553 [super drawRect:rect]; | |
554 | |
555 // Draw a mouse hover gradient for the default themes | |
556 if (!selected) { | |
557 if (![theme backgroundImageForStyle:GTMThemeStyleTabBarDeselected | |
558 state:GTMThemeStateActiveWindow]) { | |
559 scoped_nsobject<NSGradient> glow([NSGradient alloc]); | |
560 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 | |
561 alpha:1.0 * | |
562 hoverAlpha_] | |
563 endingColor:[NSColor colorWithCalibratedWhite:1.0 | |
564 alpha:0.0]]; | |
565 | |
566 NSPoint point = hoverPoint_; | |
567 point.y = NSHeight(rect); | |
568 [glow drawFromCenter:point | |
569 radius:0 | |
570 toCenter:point | |
571 radius:NSWidth(rect)/3 | |
572 options:NSGradientDrawsBeforeStartingLocation]; | |
573 | |
574 [glow drawInBezierPath:path relativeCenterPosition:hoverPoint_]; | |
575 } | |
576 } | |
577 | |
578 CGContextEndTransparencyLayer(context); | |
579 [[NSGraphicsContext currentContext] restoreGraphicsState]; | |
580 } | |
581 | |
582 // Draw the top inner highlight. | |
583 NSAffineTransform* highlightTransform = [NSAffineTransform transform]; | |
584 [highlightTransform translateXBy:1 yBy:-1]; | |
585 scoped_nsobject<NSBezierPath> highlightPath([path copy]); | |
586 [highlightPath transformUsingAffineTransform:highlightTransform]; | |
587 [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2 + 0.3 * hoverAlpha_] | |
588 setStroke]; | |
589 [highlightPath stroke]; | |
590 | |
591 [[NSGraphicsContext currentContext] restoreGraphicsState]; | |
592 | |
593 // Draw the top stroke. | |
594 [[NSGraphicsContext currentContext] saveGraphicsState]; | |
595 if (selected) { | |
596 [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.3 : 0.15] set]; | |
597 } else { | |
598 [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.2 : 0.15] set]; | |
599 [[NSBezierPath bezierPathWithRect:NSOffsetRect(rect, 0, 2.5)] addClip]; | |
600 } | |
601 [path setLineWidth:1.0]; | |
602 [path stroke]; | |
603 [[NSGraphicsContext currentContext] restoreGraphicsState]; | 514 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
604 | 515 |
605 // Draw the bottom border. | 516 // Draw the bottom border. |
606 if (!selected) { | 517 if (!selected) { |
607 [path addClip]; | 518 [path addClip]; |
608 NSRect borderRect, contentRect; | 519 NSRect borderRect, contentRect; |
609 NSDivideRect(rect, &borderRect, &contentRect, 2.5, NSMinYEdge); | 520 NSDivideRect(rect, &borderRect, &contentRect, 1, NSMinYEdge); |
610 [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.3 : 0.15] set]; | 521 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.4] set]; |
611 NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); | 522 NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); |
612 } | 523 } |
613 [[NSGraphicsContext currentContext] restoreGraphicsState]; | 524 [[NSGraphicsContext currentContext] restoreGraphicsState]; |
614 } | 525 } |
615 | 526 |
616 // Called when the user hits the right mouse button (or control-clicks) to | 527 // Called when the user hits the right mouse button (or control-clicks) to |
617 // show a context menu. | 528 // show a context menu. |
618 - (void)rightMouseDown:(NSEvent*)theEvent { | 529 - (void)rightMouseDown:(NSEvent*)theEvent { |
619 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; | 530 [NSMenu popUpContextMenu:[self menu] withEvent:theEvent forView:self]; |
620 } | 531 } |
621 | 532 |
622 @end | 533 @end |
OLD | NEW |