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

Side by Side Diff: chrome/browser/ui/cocoa/hover_close_button.mm

Issue 7331042: fix for 27454 - Fade out close button state on mouse exit (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix up rsesek nits Created 9 years, 5 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/cocoa/hover_close_button.h ('k') | chrome/browser/ui/cocoa/tabpose_window.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/hover_close_button.h" 5 #import "chrome/browser/ui/cocoa/hover_close_button.h"
6 6
7 #import <QuartzCore/QuartzCore.h>
8
7 #include "base/memory/scoped_nsobject.h" 9 #include "base/memory/scoped_nsobject.h"
10 #include "base/memory/scoped_ptr.h"
11 #import "chrome/browser/ui/cocoa/animation_utils.h"
8 #include "grit/generated_resources.h" 12 #include "grit/generated_resources.h"
9 #import "third_party/molokocacao/NSBezierPath+MCAdditions.h" 13 #import "third_party/molokocacao/NSBezierPath+MCAdditions.h"
10 #include "ui/base/l10n/l10n_util.h" 14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
11 16
12 namespace { 17 namespace {
13 const CGFloat kCircleRadius = 0.415 * 16; 18 const CGFloat kButtonWidth = 16;
19 const CGFloat kCircleRadius = 0.415 * kButtonWidth;
14 const CGFloat kCircleHoverWhite = 0.565; 20 const CGFloat kCircleHoverWhite = 0.565;
15 const CGFloat kCircleClickWhite = 0.396; 21 const CGFloat kCircleClickWhite = 0.396;
16 const CGFloat kXShadowAlpha = 0.75; 22 const CGFloat kXShadowAlpha = 0.75;
17 const CGFloat kXShadowCircleAlpha = 0.1; 23 const CGFloat kXShadowCircleAlpha = 0.1;
24
25 // Images that are used for all close buttons. Set up in +initialize.
26 CGImageRef gHoverNoneImage = NULL;
27 CGImageRef gHoverMouseOverImage = NULL;
28 CGImageRef gHoverMouseDownImage = NULL;
29
30 // Strings that are used for all close buttons. Set up in +initialize.
31 NSString* gTooltip = nil;
32 NSString* gDescription = nil;
18 } // namespace 33 } // namespace
19 34
20 @interface HoverCloseButton(Private) 35 @interface HoverCloseButton ()
21 - (void)updatePaths; 36 // Sets up the button's tracking areas and accessibility info when instantiated
22 - (void)setUpDrawingPaths; 37 // via initWithFrame or awakeFromNib.
38 - (void)commonInit;
39
40 // Draws the close button into a CGImageRef in a given state.
41 + (CGImageRef)imageForBounds:(NSRect)bounds
42 xPath:(NSBezierPath*)xPath
43 circlePath:(NSBezierPath*)circlePath
44 hoverState:(HoverState)hoverState;
23 @end 45 @end
24 46
25 @implementation HoverCloseButton 47 @implementation HoverCloseButton
26 48
49 + (void)initialize {
50 if ([self class] == [HoverCloseButton class]) {
51 // Set up the paths for our images. They are centered around the origin.
52 NSRect bounds = NSMakeRect(0, 0, kButtonWidth, kButtonWidth);
53 NSBezierPath* circlePath = [NSBezierPath bezierPath];
54 [circlePath appendBezierPathWithArcWithCenter:NSZeroPoint
55 radius:kCircleRadius
56 startAngle:0.0
57 endAngle:365.0];
58
59 // Construct an 'x' by drawing two intersecting rectangles in the shape of a
60 // cross and then rotating the path by 45 degrees.
61 NSBezierPath* xPath = [NSBezierPath bezierPath];
62 [xPath appendBezierPathWithRect:NSMakeRect(-4.5, -1.0, 9.0, 2.0)];
63 [xPath appendBezierPathWithRect:NSMakeRect(-1.0, -4.5, 2.0, 9.0)];
64
65 NSAffineTransform* transform = [NSAffineTransform transform];
66 [transform rotateByDegrees:45.0];
67 [xPath transformUsingAffineTransform:transform];
68
69 // Move the paths into the center of the given bounds rectangle.
70 transform = [NSAffineTransform transform];
71 NSPoint xCenter = NSMakePoint(NSWidth(bounds) / 2.0,
72 NSHeight(bounds) / 2.0);
73 [transform translateXBy:xCenter.x yBy:xCenter.y];
74 [circlePath transformUsingAffineTransform:transform];
75 [xPath transformUsingAffineTransform:transform];
76
77 CGImageRef image = [self imageForBounds:bounds
78 xPath:xPath
79 circlePath:circlePath
80 hoverState:kHoverStateNone];
81 gHoverNoneImage = CGImageRetain(image);
82 image = [self imageForBounds:bounds
83 xPath:xPath
84 circlePath:circlePath
85 hoverState:kHoverStateMouseOver];
86 gHoverMouseOverImage = CGImageRetain(image);
87 image = [self imageForBounds:bounds
88 xPath:xPath
89 circlePath:circlePath
90 hoverState:kHoverStateMouseDown];
91 gHoverMouseDownImage = CGImageRetain(image);
92
93 // Grab some strings that are used by all close buttons.
94 gDescription = [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE) copy];
95 gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy];
96 }
97 }
98
27 - (id)initWithFrame:(NSRect)frameRect { 99 - (id)initWithFrame:(NSRect)frameRect {
28 if ((self = [super initWithFrame:frameRect])) { 100 if ((self = [super initWithFrame:frameRect])) {
29 [self commonInit]; 101 [self commonInit];
30 } 102 }
31 return self; 103 return self;
32 } 104 }
33 105
34 - (void)awakeFromNib { 106 - (void)awakeFromNib {
35 [super awakeFromNib]; 107 [super awakeFromNib];
36 [self commonInit]; 108 [self commonInit];
37 } 109 }
38 110
39 - (void)drawRect:(NSRect)rect { 111 - (void)setHoverState:(HoverState)state {
40 if (!circlePath_.get() || !xPath_.get()) 112 [super setHoverState:state];
41 [self setUpDrawingPaths];
42 113
43 // Only call updatePaths if the size changed. 114 // Only animate the HoverStateNone case.
44 if (!NSEqualSizes(oldSize_, [self bounds].size)) 115 scoped_ptr<ScopedCAActionDisabler> disabler;
45 [self updatePaths]; 116 if (state != kHoverStateNone) {
117 disabler.reset(new ScopedCAActionDisabler);
118 }
119 [hoverNoneLayer_ setHidden:state != kHoverStateNone];
120 [hoverMouseDownLayer_ setHidden:state != kHoverStateMouseDown];
121 [hoverMouseOverLayer_ setHidden:state != kHoverStateMouseOver];
122 }
123
124 - (void)commonInit {
125 // Set accessibility description.
126 NSCell* cell = [self cell];
127 [cell accessibilitySetOverrideValue:gDescription
128 forAttribute:NSAccessibilityDescriptionAttribute];
129
130 // Add a tooltip.
131 [self setToolTip:gTooltip];
132
133 // Set up layers
134 CALayer* viewLayer = [self layer];
135
136 // Layers will be contrained to be 0 pixels from left edge horizontally,
137 // and centered vertically.
138 viewLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
139 CAConstraint* xConstraint =
140 [CAConstraint constraintWithAttribute:kCAConstraintMinX
141 relativeTo:@"superlayer"
142 attribute:kCAConstraintMinX
143 offset:0];
144 CAConstraint* yConstraint =
145 [CAConstraint constraintWithAttribute:kCAConstraintMidY
146 relativeTo:@"superlayer"
147 attribute:kCAConstraintMidY];
148
149 hoverNoneLayer_.reset([[CALayer alloc] init]);
150 [hoverNoneLayer_ setDelegate:self];
151
152 // .get() is being used here (and below) because if it isn't used, the
153 // compiler doesn't realize that the call to setFrame: is being performed
154 // on a CALayer, and assumes that the call is being performed on a NSView.
155 // setFrame: on NSView takes an NSRect, setFrame: on CALayer takes a CGRect.
156 // The difference in arguments causes a compile error.
157 [hoverNoneLayer_.get() setFrame:viewLayer.frame];
158 [viewLayer addSublayer:hoverNoneLayer_];
159 [hoverNoneLayer_ addConstraint:xConstraint];
160 [hoverNoneLayer_ addConstraint:yConstraint];
161 [hoverNoneLayer_ setContents:reinterpret_cast<id>(gHoverNoneImage)];
162 [hoverNoneLayer_ setHidden:NO];
163
164 hoverMouseOverLayer_.reset([[CALayer alloc] init]);
165 [hoverMouseOverLayer_.get() setFrame:viewLayer.frame];
166 [viewLayer addSublayer:hoverMouseOverLayer_];
167 [hoverMouseOverLayer_ addConstraint:xConstraint];
168 [hoverMouseOverLayer_ addConstraint:yConstraint];
169 [hoverMouseOverLayer_ setContents:reinterpret_cast<id>(gHoverMouseOverImage)];
170 [hoverMouseOverLayer_ setHidden:YES];
171
172 hoverMouseDownLayer_.reset([[CALayer alloc] init]);
173 [hoverMouseDownLayer_.get() setFrame:viewLayer.frame];
174 [viewLayer addSublayer:hoverMouseDownLayer_];
175 [hoverMouseDownLayer_ addConstraint:xConstraint];
176 [hoverMouseDownLayer_ addConstraint:yConstraint];
177 [hoverMouseDownLayer_ setContents:reinterpret_cast<id>(gHoverMouseDownImage)];
178 [hoverMouseDownLayer_ setHidden:YES];
179 }
180
181 + (CGImageRef)imageForBounds:(NSRect)bounds
182 xPath:(NSBezierPath*)xPath
183 circlePath:(NSBezierPath*)circlePath
184 hoverState:(HoverState)hoverState {
185 gfx::ScopedNSGraphicsContextSaveGState graphicsStateSaver;
186
187 scoped_nsobject<NSBitmapImageRep> imageRep(
188 [[NSBitmapImageRep alloc]
189 initWithBitmapDataPlanes:NULL
190 pixelsWide:NSWidth(bounds)
191 pixelsHigh:NSHeight(bounds)
192 bitsPerSample:8
193 samplesPerPixel:4
194 hasAlpha:YES
195 isPlanar:NO
196 colorSpaceName:NSCalibratedRGBColorSpace
197 bitmapFormat:0
198 bytesPerRow:kButtonWidth * 4
199 bitsPerPixel:32]);
200 NSGraphicsContext* gc =
201 [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
202 [NSGraphicsContext setCurrentContext:gc];
203
204 [[NSColor clearColor] set];
205 NSRectFill(bounds);
46 206
47 // If the user is hovering over the button, a light/dark gray circle is drawn 207 // If the user is hovering over the button, a light/dark gray circle is drawn
48 // behind the 'x'. 208 // behind the 'x'.
49 if (hoverState_ != kHoverStateNone) { 209 if (hoverState != kHoverStateNone) {
50 // Adjust the darkness of the circle depending on whether it is being 210 // Adjust the darkness of the circle depending on whether it is being
51 // clicked. 211 // clicked.
52 CGFloat white = (hoverState_ == kHoverStateMouseOver) ? 212 CGFloat white = (hoverState == kHoverStateMouseOver) ?
53 kCircleHoverWhite : kCircleClickWhite; 213 kCircleHoverWhite : kCircleClickWhite;
54 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set]; 214 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set];
55 [circlePath_ fill]; 215 [circlePath fill];
56 } 216 }
57 217
58 [[NSColor whiteColor] set]; 218 [[NSColor whiteColor] set];
59 [xPath_ fill]; 219 [xPath fill];
60 220
61 // Give the 'x' an inner shadow for depth. If the button is in a hover state 221 // Give the 'x' an inner shadow for depth. If the button is in a hover state
62 // (circle behind it), then adjust the shadow accordingly (not as harsh). 222 // (circle behind it), then adjust the shadow accordingly (not as harsh).
63 NSShadow* shadow = [[[NSShadow alloc] init] autorelease]; 223 NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
64 CGFloat alpha = (hoverState_ != kHoverStateNone) ? 224 CGFloat alpha = (hoverState != kHoverStateNone) ?
65 kXShadowCircleAlpha : kXShadowAlpha; 225 kXShadowCircleAlpha : kXShadowAlpha;
66 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 226 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 alpha:alpha]];
67 alpha:alpha]];
68 [shadow setShadowOffset:NSMakeSize(0.0, 0.0)]; 227 [shadow setShadowOffset:NSMakeSize(0.0, 0.0)];
69 [shadow setShadowBlurRadius:2.5]; 228 [shadow setShadowBlurRadius:2.5];
70 [xPath_ fillWithInnerShadow:shadow]; 229 [xPath fillWithInnerShadow:shadow];
71 }
72 230
73 - (void)commonInit { 231 // CGImage returns an autoreleased CGImageRef.
74 // Set accessibility description. 232 return [imageRep CGImage];
75 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE);
76 [[self cell]
77 accessibilitySetOverrideValue:description
78 forAttribute:NSAccessibilityDescriptionAttribute];
79
80 // Add a tooltip.
81 [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB)];
82 }
83
84 - (void)setUpDrawingPaths {
85 // Keep the paths centered around the origin in this function. It is then
86 // translated in -updatePaths.
87 NSPoint xCenter = NSZeroPoint;
88
89 circlePath_.reset([[NSBezierPath bezierPath] retain]);
90 [circlePath_ moveToPoint:xCenter];
91 CGFloat radius = kCircleRadius;
92 [circlePath_ appendBezierPathWithArcWithCenter:xCenter
93 radius:radius
94 startAngle:0.0
95 endAngle:365.0];
96
97 // Construct an 'x' by drawing two intersecting rectangles in the shape of a
98 // cross and then rotating the path by 45 degrees.
99 xPath_.reset([[NSBezierPath bezierPath] retain]);
100 [xPath_ appendBezierPathWithRect:NSMakeRect(3.5, 7.0, 9.0, 2.0)];
101 [xPath_ appendBezierPathWithRect:NSMakeRect(7.0, 3.5, 2.0, 9.0)];
102
103 NSRect pathBounds = [xPath_ bounds];
104 NSPoint pathCenter = NSMakePoint(NSMidX(pathBounds), NSMidY(pathBounds));
105
106 NSAffineTransform* transform = [NSAffineTransform transform];
107 [transform translateXBy:xCenter.x yBy:xCenter.y];
108 [transform rotateByDegrees:45.0];
109 [transform translateXBy:-pathCenter.x yBy:-pathCenter.y];
110
111 [xPath_ transformUsingAffineTransform:transform];
112 }
113
114 - (void)updatePaths {
115 oldSize_ = [self bounds].size;
116
117 // Revert the current transform for the two points.
118 if (transform_.get()) {
119 [transform_.get() invert];
120 [circlePath_.get() transformUsingAffineTransform:transform_.get()];
121 [xPath_.get() transformUsingAffineTransform:transform_.get()];
122 }
123
124 // Create the new transform. [self bounds] is prefered in case aRect wasn't
125 // literally taken as bounds (e.g. cropped).
126 NSPoint xCenter = NSMakePoint(8, oldSize_.height / 2.0f);
127
128 // Retain here, as scoped_* don't retain.
129 transform_.reset([[NSAffineTransform transform] retain]);
130 [transform_.get() translateXBy:xCenter.x yBy:xCenter.y];
131 [circlePath_.get() transformUsingAffineTransform:transform_.get()];
132 [xPath_.get() transformUsingAffineTransform:transform_.get()];
133 } 233 }
134 234
135 @end 235 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/hover_close_button.h ('k') | chrome/browser/ui/cocoa/tabpose_window.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698