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

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: 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"
11 15
12 namespace { 16 namespace {
13 const CGFloat kCircleRadius = 0.415 * 16; 17 const CGFloat kButtonWidth = 16;
18 const CGFloat kCircleRadius = 0.415 * kButtonWidth;
14 const CGFloat kCircleHoverWhite = 0.565; 19 const CGFloat kCircleHoverWhite = 0.565;
15 const CGFloat kCircleClickWhite = 0.396; 20 const CGFloat kCircleClickWhite = 0.396;
16 const CGFloat kXShadowAlpha = 0.75; 21 const CGFloat kXShadowAlpha = 0.75;
17 const CGFloat kXShadowCircleAlpha = 0.1; 22 const CGFloat kXShadowCircleAlpha = 0.1;
23
24 // Images that are used for all close buttons. Set up in +initialize.
25 CGImageRef gHoverNoneImage = NULL;
26 CGImageRef gHoverMouseOverImage = NULL;
27 CGImageRef gHoverMouseDownImage = NULL;
28
29 // Strings that are used for all close buttons. Set up in +initialize.
30 NSString* gTooltip = nil;
31 NSString* gDescription = nil;
18 } // namespace 32 } // namespace
19 33
20 @interface HoverCloseButton(Private) 34 @interface HoverCloseButton ()
21 - (void)updatePaths; 35 // Sets up the button's tracking areas and accessibility info when instantiated
22 - (void)setUpDrawingPaths; 36 // via initWithFrame or awakeFromNib.
37 - (void)commonInit;
38
39 // Draws the close button into a CGImageRef in a given state.
40 + (CGImageRef)imageForBounds:(NSRect)bounds
41 xPath:(NSBezierPath*)xPath
42 circlePath:(NSBezierPath*)circlePath
43 hoverState:(HoverState)hoverState;
23 @end 44 @end
24 45
25 @implementation HoverCloseButton 46 @implementation HoverCloseButton
26 47
48 + (void)initialize {
49 if ([self class] == [HoverCloseButton class]) {
50 // Set up the paths for our images. They are centered around the origin.
51 NSRect bounds = NSMakeRect(0, 0, kButtonWidth, kButtonWidth);
52 NSBezierPath* circlePath = [NSBezierPath bezierPath];
53 [circlePath appendBezierPathWithArcWithCenter:NSZeroPoint
54 radius:kCircleRadius
55 startAngle:0.0
56 endAngle:365.0];
57
58 // Construct an 'x' by drawing two intersecting rectangles in the shape of a
59 // cross and then rotating the path by 45 degrees.
60 NSBezierPath* xPath = [NSBezierPath bezierPath];
61 [xPath appendBezierPathWithRect:NSMakeRect(-4.5, -1.0, 9.0, 2.0)];
62 [xPath appendBezierPathWithRect:NSMakeRect(-1.0, -4.5, 2.0, 9.0)];
63
64 NSAffineTransform* transform = [NSAffineTransform transform];
65 [transform rotateByDegrees:45.0];
66 [xPath transformUsingAffineTransform:transform];
67
68 // Move the paths into the center of the given bounds rectangle.
69 transform = [NSAffineTransform transform];
70 NSPoint xCenter = NSMakePoint(NSWidth(bounds) / 2.0,
71 NSHeight(bounds) / 2.0);
72 [transform translateXBy:xCenter.x yBy:xCenter.y];
73 [circlePath transformUsingAffineTransform:transform];
74 [xPath transformUsingAffineTransform:transform];
75
76 CGImageRef image = [self imageForBounds:bounds
77 xPath:xPath
78 circlePath:circlePath
79 hoverState:kHoverStateNone];
80 gHoverNoneImage = CGImageRetain(image);
81 image = [self imageForBounds:bounds
82 xPath:xPath
83 circlePath:circlePath
84 hoverState:kHoverStateMouseOver];
85 gHoverMouseOverImage = CGImageRetain(image);
86 image = [self imageForBounds:bounds
87 xPath:xPath
88 circlePath:circlePath
89 hoverState:kHoverStateMouseDown];
90 gHoverMouseDownImage = CGImageRetain(image);
91
92 // Grab some strings that are used by all close buttons.
93 gDescription = [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE) copy];
94 gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy];
95 }
96 }
97
27 - (id)initWithFrame:(NSRect)frameRect { 98 - (id)initWithFrame:(NSRect)frameRect {
28 if ((self = [super initWithFrame:frameRect])) { 99 if ((self = [super initWithFrame:frameRect])) {
29 [self commonInit]; 100 [self commonInit];
30 } 101 }
31 return self; 102 return self;
32 } 103 }
33 104
34 - (void)awakeFromNib { 105 - (void)awakeFromNib {
35 [super awakeFromNib]; 106 [super awakeFromNib];
36 [self commonInit]; 107 [self commonInit];
37 } 108 }
38 109
39 - (void)drawRect:(NSRect)rect { 110 - (void)setHoverState:(HoverState)state {
40 if (!circlePath_.get() || !xPath_.get()) 111 [super setHoverState:state];
41 [self setUpDrawingPaths];
42 112
43 // Only call updatePaths if the size changed. 113 // Only animate the HoverStateNone case.
44 if (!NSEqualSizes(oldSize_, [self bounds].size)) 114 scoped_ptr<ScopedCAActionDisabler> disabler;
45 [self updatePaths]; 115 if (state != kHoverStateNone) {
116 disabler.reset(new ScopedCAActionDisabler);
117 }
118 [hoverNoneLayer_ setHidden:state != kHoverStateNone];
119 [hoverMouseDownLayer_ setHidden:state != kHoverStateMouseDown];
120 [hoverMouseOverLayer_ setHidden:state != kHoverStateMouseOver];
121 }
122
123 - (void)commonInit {
124 // Set accessibility description.
125 NSCell* cell = [self cell];
126 [cell accessibilitySetOverrideValue:gDescription
127 forAttribute:NSAccessibilityDescriptionAttribute];
128
129 // Add a tooltip.
130 [self setToolTip:gTooltip];
131
132 // Set up layers
133 CALayer* viewLayer = [self layer];
134
135 // Layers will be contrained to be 8 pixels from left edge horizontally,
136 // and centered vertically.
Miranda Callahan 2011/07/12 13:44:35 Is it really 8 pixels from the left edge? I don't
137 viewLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
138 CAConstraint* xConstraint =
139 [CAConstraint constraintWithAttribute:kCAConstraintMinX
140 relativeTo:@"superlayer"
141 attribute:kCAConstraintMinX
142 offset:0];
143 CAConstraint* yConstraint =
144 [CAConstraint constraintWithAttribute:kCAConstraintMidY
145 relativeTo:@"superlayer"
146 attribute:kCAConstraintMidY];
147
148 hoverNoneLayer_.reset([[CALayer alloc] init]);
149 [hoverNoneLayer_ setDelegate:self];
150 [hoverNoneLayer_.get() setFrame:viewLayer.frame];
151 [viewLayer addSublayer:hoverNoneLayer_];
152 [hoverNoneLayer_ addConstraint:xConstraint];
153 [hoverNoneLayer_ addConstraint:yConstraint];
154 [hoverNoneLayer_ setContents:reinterpret_cast<id>(gHoverNoneImage)];
155 [hoverNoneLayer_ setHidden:NO];
156
157 hoverMouseOverLayer_.reset([[CALayer alloc] init]);
158 [hoverMouseOverLayer_.get() setFrame:viewLayer.frame];
159 [viewLayer addSublayer:hoverMouseOverLayer_];
160 [hoverMouseOverLayer_ addConstraint:xConstraint];
161 [hoverMouseOverLayer_ addConstraint:yConstraint];
162 [hoverMouseOverLayer_ setContents:reinterpret_cast<id>(gHoverMouseOverImage)];
163 [hoverMouseOverLayer_ setHidden:YES];
164
165 hoverMouseDownLayer_.reset([[CALayer alloc] init]);
166 [hoverMouseDownLayer_.get() setFrame:viewLayer.frame];
167 [viewLayer addSublayer:hoverMouseDownLayer_];
168 [hoverMouseDownLayer_ addConstraint:xConstraint];
169 [hoverMouseDownLayer_ addConstraint:yConstraint];
170 [hoverMouseDownLayer_ setContents:reinterpret_cast<id>(gHoverMouseDownImage)];
171 [hoverMouseDownLayer_ setHidden:YES];
172 }
173
174 + (CGImageRef)imageForBounds:(NSRect)bounds
175 xPath:(NSBezierPath*)xPath
176 circlePath:(NSBezierPath*)circlePath
177 hoverState:(HoverState)hoverState {
178 [NSGraphicsContext saveGraphicsState];
179 scoped_nsobject<NSBitmapImageRep> imageRep(
180 [[NSBitmapImageRep alloc]
181 initWithBitmapDataPlanes:NULL
182 pixelsWide:NSWidth(bounds)
183 pixelsHigh:NSHeight(bounds)
184 bitsPerSample:8
185 samplesPerPixel:4
186 hasAlpha:YES
187 isPlanar:NO
188 colorSpaceName:NSCalibratedRGBColorSpace
189 bitmapFormat:0
190 bytesPerRow:kButtonWidth * 4
191 bitsPerPixel:32]);
192 NSGraphicsContext* gc =
193 [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
194 [NSGraphicsContext setCurrentContext:gc];
195
196 [[NSColor clearColor] set];
197 NSRectFill(bounds);
46 198
47 // If the user is hovering over the button, a light/dark gray circle is drawn 199 // If the user is hovering over the button, a light/dark gray circle is drawn
48 // behind the 'x'. 200 // behind the 'x'.
49 if (hoverState_ != kHoverStateNone) { 201 if (hoverState != kHoverStateNone) {
50 // Adjust the darkness of the circle depending on whether it is being 202 // Adjust the darkness of the circle depending on whether it is being
51 // clicked. 203 // clicked.
52 CGFloat white = (hoverState_ == kHoverStateMouseOver) ? 204 CGFloat white = (hoverState == kHoverStateMouseOver) ?
53 kCircleHoverWhite : kCircleClickWhite; 205 kCircleHoverWhite : kCircleClickWhite;
54 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set]; 206 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set];
55 [circlePath_ fill]; 207 [circlePath fill];
56 } 208 }
57 209
58 [[NSColor whiteColor] set]; 210 [[NSColor whiteColor] set];
59 [xPath_ fill]; 211 [xPath fill];
60 212
61 // Give the 'x' an inner shadow for depth. If the button is in a hover state 213 // 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). 214 // (circle behind it), then adjust the shadow accordingly (not as harsh).
63 NSShadow* shadow = [[[NSShadow alloc] init] autorelease]; 215 NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
64 CGFloat alpha = (hoverState_ != kHoverStateNone) ? 216 CGFloat alpha = (hoverState != kHoverStateNone) ?
65 kXShadowCircleAlpha : kXShadowAlpha; 217 kXShadowCircleAlpha : kXShadowAlpha;
66 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 218 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 alpha:alpha]];
67 alpha:alpha]];
68 [shadow setShadowOffset:NSMakeSize(0.0, 0.0)]; 219 [shadow setShadowOffset:NSMakeSize(0.0, 0.0)];
69 [shadow setShadowBlurRadius:2.5]; 220 [shadow setShadowBlurRadius:2.5];
70 [xPath_ fillWithInnerShadow:shadow]; 221 [xPath fillWithInnerShadow:shadow];
71 } 222 [NSGraphicsContext restoreGraphicsState];
72 223 return [imageRep CGImage];
73 - (void)commonInit {
74 // Set accessibility description.
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 } 224 }
134 225
135 @end 226 @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