| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/cocoa/hover_close_button.h" | 5 #import "chrome/browser/cocoa/hover_close_button.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/nsimage_cache_mac.h" | 8 #include "base/scoped_nsobject.h" |
| 9 #import "chrome/browser/cocoa/third_party/NSBezierPath+MCAdditions.h" |
| 9 #include "grit/generated_resources.h" | 10 #include "grit/generated_resources.h" |
| 10 | 11 |
| 11 namespace { | 12 namespace { |
| 13 // Convenience function to return the middle point of the given |rect|. |
| 14 static NSPoint MidRect(NSRect rect) { |
| 15 return NSMakePoint(NSMidX(rect), NSMidY(rect)); |
| 16 } |
| 12 | 17 |
| 13 NSString* const kNormalImageString = @"close_bar.pdf"; | 18 const CGFloat kCircleRadiusPercentage = 0.45; |
| 14 NSString* const kHoverImageString = @"close_bar_h.pdf"; | 19 const CGFloat kCircleHoverWhite = 0.565; |
| 15 NSString* const kPressedImageString = @"close_bar_p.pdf"; | 20 const CGFloat kCircleClickWhite = 0.396; |
| 21 const CGFloat kXShadowAlpha = 0.6; |
| 22 const CGFloat kXShadowCircleAlpha = 0.1; |
| 23 } // namespace |
| 16 | 24 |
| 17 } // namespace | 25 @interface HoverCloseButton(Private) |
| 26 - (void)setUpDrawingPaths; |
| 27 @end |
| 18 | 28 |
| 19 @implementation HoverCloseButton | 29 @implementation HoverCloseButton |
| 20 | 30 |
| 21 - (id)initWithFrame:(NSRect)frameRect { | 31 - (id)initWithFrame:(NSRect)frameRect { |
| 22 if ((self = [super initWithFrame:frameRect])) { | 32 if ((self = [super initWithFrame:frameRect])) { |
| 23 [self commonInit]; | 33 [self commonInit]; |
| 24 } | 34 } |
| 25 return self; | 35 return self; |
| 26 } | 36 } |
| 27 | 37 |
| 28 - (void)awakeFromNib { | 38 - (void)awakeFromNib { |
| 29 [self commonInit]; | 39 [self commonInit]; |
| 30 } | 40 } |
| 31 | 41 |
| 42 - (void)drawRect:(NSRect)rect { |
| 43 if (!circlePath_.get() || !xPath_.get()) |
| 44 [self setUpDrawingPaths]; |
| 45 |
| 46 // If the user is hovering over the button, a light/dark gray circle is drawn |
| 47 // behind the 'x'. |
| 48 if (hoverState_ != kHoverStateNone) { |
| 49 // Adjust the darkness of the circle depending on whether it is being |
| 50 // clicked. |
| 51 CGFloat white = (hoverState_ == kHoverStateMouseOver) ? |
| 52 kCircleHoverWhite : kCircleClickWhite; |
| 53 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set]; |
| 54 [circlePath_ fill]; |
| 55 } |
| 56 |
| 57 [[NSColor whiteColor] set]; |
| 58 [xPath_ fill]; |
| 59 |
| 60 // Give the 'x' an inner shadow for depth. If the button is in a hover state |
| 61 // (circle behind it), then adjust the shadow accordingly (not as harsh). |
| 62 NSShadow* shadow = [[NSShadow alloc] init]; |
| 63 CGFloat alpha = (hoverState_ != kHoverStateNone) ? |
| 64 kXShadowCircleAlpha : kXShadowAlpha; |
| 65 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 |
| 66 alpha:alpha]]; |
| 67 [shadow setShadowOffset:NSMakeSize(0.0, -1.0)]; |
| 68 [shadow setShadowBlurRadius:2.0]; |
| 69 [xPath_ fillWithInnerShadow:shadow]; |
| 70 } |
| 71 |
| 32 - (void)commonInit { | 72 - (void)commonInit { |
| 33 [self setTrackingEnabled:YES]; | 73 [self setTrackingEnabled:YES]; |
| 34 NSImage* alternateImage = nsimage_cache::ImageNamed(kPressedImageString); | 74 hoverState_ = kHoverStateNone; |
| 35 [self setAlternateImage:alternateImage]; | |
| 36 [self updateTrackingAreas]; | 75 [self updateTrackingAreas]; |
| 37 | 76 |
| 38 // Set accessibility description. | 77 // Set accessibility description. |
| 39 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE); | 78 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE); |
| 40 [[self cell] | 79 [[self cell] |
| 41 accessibilitySetOverrideValue:description | 80 accessibilitySetOverrideValue:description |
| 42 forAttribute:NSAccessibilityDescriptionAttribute]; | 81 forAttribute:NSAccessibilityDescriptionAttribute]; |
| 43 } | 82 } |
| 44 | 83 |
| 84 - (void)setUpDrawingPaths { |
| 85 NSPoint viewCenter = MidRect([self bounds]); |
| 86 |
| 87 circlePath_.reset([[NSBezierPath bezierPath] retain]); |
| 88 [circlePath_ moveToPoint:viewCenter]; |
| 89 CGFloat radius = kCircleRadiusPercentage * NSWidth([self bounds]); |
| 90 [circlePath_ appendBezierPathWithArcWithCenter:viewCenter |
| 91 radius:radius |
| 92 startAngle:0.0 |
| 93 endAngle:365.0]; |
| 94 |
| 95 // Construct an 'x' by drawing two intersecting rectangles in the shape of a |
| 96 // cross and then rotating the path by 45 degrees. |
| 97 xPath_.reset([[NSBezierPath bezierPath] retain]); |
| 98 [xPath_ appendBezierPathWithRect:NSMakeRect(3.5, 7.0, 9.0, 2.0)]; |
| 99 [xPath_ appendBezierPathWithRect:NSMakeRect(7.0, 3.5, 2.0, 9.0)]; |
| 100 |
| 101 NSPoint pathCenter = MidRect([xPath_ bounds]); |
| 102 |
| 103 NSAffineTransform* transform = [NSAffineTransform transform]; |
| 104 [transform translateXBy:viewCenter.x yBy:viewCenter.y]; |
| 105 [transform rotateByDegrees:45.0]; |
| 106 [transform translateXBy:-pathCenter.x yBy:-pathCenter.y]; |
| 107 |
| 108 [xPath_ transformUsingAffineTransform:transform]; |
| 109 } |
| 110 |
| 45 - (void)dealloc { | 111 - (void)dealloc { |
| 46 [self setTrackingEnabled:NO]; | 112 [self setTrackingEnabled:NO]; |
| 47 [super dealloc]; | 113 [super dealloc]; |
| 48 } | 114 } |
| 49 | 115 |
| 50 - (void)mouseEntered:(NSEvent*)theEvent { | 116 - (void)mouseEntered:(NSEvent*)theEvent { |
| 51 [self setImage:nsimage_cache::ImageNamed(kHoverImageString)]; | 117 hoverState_ = kHoverStateMouseOver; |
| 118 [self setNeedsDisplay:YES]; |
| 52 } | 119 } |
| 53 | 120 |
| 54 - (void)mouseExited:(NSEvent*)theEvent { | 121 - (void)mouseExited:(NSEvent*)theEvent { |
| 55 [self setImage:nsimage_cache::ImageNamed(kNormalImageString)]; | 122 hoverState_ = kHoverStateNone; |
| 123 [self setNeedsDisplay:YES]; |
| 56 } | 124 } |
| 57 | 125 |
| 58 - (void)mouseDown:(NSEvent*)theEvent { | 126 - (void)mouseDown:(NSEvent*)theEvent { |
| 127 hoverState_ = kHoverStateMouseDown; |
| 128 [self setNeedsDisplay:YES]; |
| 59 // The hover button needs to hold onto itself here for a bit. Otherwise, | 129 // The hover button needs to hold onto itself here for a bit. Otherwise, |
| 60 // it can be freed while |super mouseDown:| is in it's loop, and the | 130 // it can be freed while |super mouseDown:| is in it's loop, and the |
| 61 // |checkImageState| call will crash. | 131 // |checkImageState| call will crash. |
| 62 // http://crbug.com/28220 | 132 // http://crbug.com/28220 |
| 63 scoped_nsobject<HoverCloseButton> myself([self retain]); | 133 scoped_nsobject<HoverCloseButton> myself([self retain]); |
| 64 | 134 |
| 65 [super mouseDown:theEvent]; | 135 [super mouseDown:theEvent]; |
| 66 // We need to check the image state after the mouseDown event loop finishes. | 136 // We need to check the image state after the mouseDown event loop finishes. |
| 67 // It's possible that we won't get a mouseExited event if the button was | 137 // It's possible that we won't get a mouseExited event if the button was |
| 68 // moved under the mouse during tab resize, instead of the mouse moving over | 138 // moved under the mouse during tab resize, instead of the mouse moving over |
| (...skipping 24 matching lines...) Expand all Loading... |
| 93 } | 163 } |
| 94 } | 164 } |
| 95 } | 165 } |
| 96 | 166 |
| 97 - (void)updateTrackingAreas { | 167 - (void)updateTrackingAreas { |
| 98 [super updateTrackingAreas]; | 168 [super updateTrackingAreas]; |
| 99 [self checkImageState]; | 169 [self checkImageState]; |
| 100 } | 170 } |
| 101 | 171 |
| 102 - (void)checkImageState { | 172 - (void)checkImageState { |
| 103 if (closeTrackingArea_.get()) { | 173 if (!closeTrackingArea_.get()) |
| 104 // Update the close buttons if the tab has moved. | 174 return; |
| 105 NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; | 175 |
| 106 mouseLoc = [self convertPoint:mouseLoc fromView:nil]; | 176 // Update the close buttons if the tab has moved. |
| 107 NSString* name = NSPointInRect(mouseLoc, [self bounds]) ? | 177 NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; |
| 108 kHoverImageString : kNormalImageString; | 178 mouseLoc = [self convertPoint:mouseLoc fromView:nil]; |
| 109 NSImage* newImage = nsimage_cache::ImageNamed(name); | 179 hoverState_ = NSPointInRect(mouseLoc, [self bounds]) ? |
| 110 NSImage* buttonImage = [self image]; | 180 kHoverStateMouseOver : kHoverStateNone; |
| 111 if (![buttonImage isEqual:newImage]) { | 181 [self setNeedsDisplay:YES]; |
| 112 [self setImage:newImage]; | |
| 113 } | |
| 114 } | |
| 115 } | 182 } |
| 116 | 183 |
| 117 @end | 184 @end |
| OLD | NEW |