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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/cocoa/hover_close_button.mm
diff --git a/chrome/browser/ui/cocoa/hover_close_button.mm b/chrome/browser/ui/cocoa/hover_close_button.mm
index 32a5c80d6ee00f6de82001982ef36add487749de..5dacaded56c589658e7a86275a7e6324179c24f0 100644
--- a/chrome/browser/ui/cocoa/hover_close_button.mm
+++ b/chrome/browser/ui/cocoa/hover_close_button.mm
@@ -4,26 +4,98 @@
#import "chrome/browser/ui/cocoa/hover_close_button.h"
+#import <QuartzCore/QuartzCore.h>
+
#include "base/memory/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#import "chrome/browser/ui/cocoa/animation_utils.h"
#include "grit/generated_resources.h"
#import "third_party/molokocacao/NSBezierPath+MCAdditions.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
namespace {
-const CGFloat kCircleRadius = 0.415 * 16;
+const CGFloat kButtonWidth = 16;
+const CGFloat kCircleRadius = 0.415 * kButtonWidth;
const CGFloat kCircleHoverWhite = 0.565;
const CGFloat kCircleClickWhite = 0.396;
const CGFloat kXShadowAlpha = 0.75;
const CGFloat kXShadowCircleAlpha = 0.1;
+
+// Images that are used for all close buttons. Set up in +initialize.
+CGImageRef gHoverNoneImage = NULL;
+CGImageRef gHoverMouseOverImage = NULL;
+CGImageRef gHoverMouseDownImage = NULL;
+
+// Strings that are used for all close buttons. Set up in +initialize.
+NSString* gTooltip = nil;
+NSString* gDescription = nil;
} // namespace
-@interface HoverCloseButton(Private)
-- (void)updatePaths;
-- (void)setUpDrawingPaths;
+@interface HoverCloseButton ()
+// Sets up the button's tracking areas and accessibility info when instantiated
+// via initWithFrame or awakeFromNib.
+- (void)commonInit;
+
+// Draws the close button into a CGImageRef in a given state.
++ (CGImageRef)imageForBounds:(NSRect)bounds
+ xPath:(NSBezierPath*)xPath
+ circlePath:(NSBezierPath*)circlePath
+ hoverState:(HoverState)hoverState;
@end
@implementation HoverCloseButton
++ (void)initialize {
+ if ([self class] == [HoverCloseButton class]) {
+ // Set up the paths for our images. They are centered around the origin.
+ NSRect bounds = NSMakeRect(0, 0, kButtonWidth, kButtonWidth);
+ NSBezierPath* circlePath = [NSBezierPath bezierPath];
+ [circlePath appendBezierPathWithArcWithCenter:NSZeroPoint
+ radius:kCircleRadius
+ startAngle:0.0
+ endAngle:365.0];
+
+ // Construct an 'x' by drawing two intersecting rectangles in the shape of a
+ // cross and then rotating the path by 45 degrees.
+ NSBezierPath* xPath = [NSBezierPath bezierPath];
+ [xPath appendBezierPathWithRect:NSMakeRect(-4.5, -1.0, 9.0, 2.0)];
+ [xPath appendBezierPathWithRect:NSMakeRect(-1.0, -4.5, 2.0, 9.0)];
+
+ NSAffineTransform* transform = [NSAffineTransform transform];
+ [transform rotateByDegrees:45.0];
+ [xPath transformUsingAffineTransform:transform];
+
+ // Move the paths into the center of the given bounds rectangle.
+ transform = [NSAffineTransform transform];
+ NSPoint xCenter = NSMakePoint(NSWidth(bounds) / 2.0,
+ NSHeight(bounds) / 2.0);
+ [transform translateXBy:xCenter.x yBy:xCenter.y];
+ [circlePath transformUsingAffineTransform:transform];
+ [xPath transformUsingAffineTransform:transform];
+
+ CGImageRef image = [self imageForBounds:bounds
+ xPath:xPath
+ circlePath:circlePath
+ hoverState:kHoverStateNone];
+ gHoverNoneImage = CGImageRetain(image);
+ image = [self imageForBounds:bounds
+ xPath:xPath
+ circlePath:circlePath
+ hoverState:kHoverStateMouseOver];
+ gHoverMouseOverImage = CGImageRetain(image);
+ image = [self imageForBounds:bounds
+ xPath:xPath
+ circlePath:circlePath
+ hoverState:kHoverStateMouseDown];
+ gHoverMouseDownImage = CGImageRetain(image);
+
+ // Grab some strings that are used by all close buttons.
+ gDescription = [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE) copy];
+ gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy];
+ }
+}
+
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect])) {
[self commonInit];
@@ -36,100 +108,128 @@ const CGFloat kXShadowCircleAlpha = 0.1;
[self commonInit];
}
-- (void)drawRect:(NSRect)rect {
- if (!circlePath_.get() || !xPath_.get())
- [self setUpDrawingPaths];
+- (void)setHoverState:(HoverState)state {
+ [super setHoverState:state];
- // Only call updatePaths if the size changed.
- if (!NSEqualSizes(oldSize_, [self bounds].size))
- [self updatePaths];
+ // Only animate the HoverStateNone case.
+ scoped_ptr<ScopedCAActionDisabler> disabler;
+ if (state != kHoverStateNone) {
+ disabler.reset(new ScopedCAActionDisabler);
+ }
+ [hoverNoneLayer_ setHidden:state != kHoverStateNone];
+ [hoverMouseDownLayer_ setHidden:state != kHoverStateMouseDown];
+ [hoverMouseOverLayer_ setHidden:state != kHoverStateMouseOver];
+}
+
+- (void)commonInit {
+ // Set accessibility description.
+ NSCell* cell = [self cell];
+ [cell accessibilitySetOverrideValue:gDescription
+ forAttribute:NSAccessibilityDescriptionAttribute];
+
+ // Add a tooltip.
+ [self setToolTip:gTooltip];
+
+ // Set up layers
+ CALayer* viewLayer = [self layer];
+
+ // Layers will be contrained to be 0 pixels from left edge horizontally,
+ // and centered vertically.
+ viewLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
+ CAConstraint* xConstraint =
+ [CAConstraint constraintWithAttribute:kCAConstraintMinX
+ relativeTo:@"superlayer"
+ attribute:kCAConstraintMinX
+ offset:0];
+ CAConstraint* yConstraint =
+ [CAConstraint constraintWithAttribute:kCAConstraintMidY
+ relativeTo:@"superlayer"
+ attribute:kCAConstraintMidY];
+
+ hoverNoneLayer_.reset([[CALayer alloc] init]);
+ [hoverNoneLayer_ setDelegate:self];
+
+ // .get() is being used here (and below) because if it isn't used, the
+ // compiler doesn't realize that the call to setFrame: is being performed
+ // on a CALayer, and assumes that the call is being performed on a NSView.
+ // setFrame: on NSView takes an NSRect, setFrame: on CALayer takes a CGRect.
+ // The difference in arguments causes a compile error.
+ [hoverNoneLayer_.get() setFrame:viewLayer.frame];
+ [viewLayer addSublayer:hoverNoneLayer_];
+ [hoverNoneLayer_ addConstraint:xConstraint];
+ [hoverNoneLayer_ addConstraint:yConstraint];
+ [hoverNoneLayer_ setContents:reinterpret_cast<id>(gHoverNoneImage)];
+ [hoverNoneLayer_ setHidden:NO];
+
+ hoverMouseOverLayer_.reset([[CALayer alloc] init]);
+ [hoverMouseOverLayer_.get() setFrame:viewLayer.frame];
+ [viewLayer addSublayer:hoverMouseOverLayer_];
+ [hoverMouseOverLayer_ addConstraint:xConstraint];
+ [hoverMouseOverLayer_ addConstraint:yConstraint];
+ [hoverMouseOverLayer_ setContents:reinterpret_cast<id>(gHoverMouseOverImage)];
+ [hoverMouseOverLayer_ setHidden:YES];
+
+ hoverMouseDownLayer_.reset([[CALayer alloc] init]);
+ [hoverMouseDownLayer_.get() setFrame:viewLayer.frame];
+ [viewLayer addSublayer:hoverMouseDownLayer_];
+ [hoverMouseDownLayer_ addConstraint:xConstraint];
+ [hoverMouseDownLayer_ addConstraint:yConstraint];
+ [hoverMouseDownLayer_ setContents:reinterpret_cast<id>(gHoverMouseDownImage)];
+ [hoverMouseDownLayer_ setHidden:YES];
+}
+
++ (CGImageRef)imageForBounds:(NSRect)bounds
+ xPath:(NSBezierPath*)xPath
+ circlePath:(NSBezierPath*)circlePath
+ hoverState:(HoverState)hoverState {
+ gfx::ScopedNSGraphicsContextSaveGState graphicsStateSaver;
+
+ scoped_nsobject<NSBitmapImageRep> imageRep(
+ [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:NSWidth(bounds)
+ pixelsHigh:NSHeight(bounds)
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSCalibratedRGBColorSpace
+ bitmapFormat:0
+ bytesPerRow:kButtonWidth * 4
+ bitsPerPixel:32]);
+ NSGraphicsContext* gc =
+ [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
+ [NSGraphicsContext setCurrentContext:gc];
+
+ [[NSColor clearColor] set];
+ NSRectFill(bounds);
// If the user is hovering over the button, a light/dark gray circle is drawn
// behind the 'x'.
- if (hoverState_ != kHoverStateNone) {
+ if (hoverState != kHoverStateNone) {
// Adjust the darkness of the circle depending on whether it is being
// clicked.
- CGFloat white = (hoverState_ == kHoverStateMouseOver) ?
+ CGFloat white = (hoverState == kHoverStateMouseOver) ?
kCircleHoverWhite : kCircleClickWhite;
[[NSColor colorWithCalibratedWhite:white alpha:1.0] set];
- [circlePath_ fill];
+ [circlePath fill];
}
[[NSColor whiteColor] set];
- [xPath_ fill];
+ [xPath fill];
// Give the 'x' an inner shadow for depth. If the button is in a hover state
// (circle behind it), then adjust the shadow accordingly (not as harsh).
NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
- CGFloat alpha = (hoverState_ != kHoverStateNone) ?
+ CGFloat alpha = (hoverState != kHoverStateNone) ?
kXShadowCircleAlpha : kXShadowAlpha;
- [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15
- alpha:alpha]];
+ [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 alpha:alpha]];
[shadow setShadowOffset:NSMakeSize(0.0, 0.0)];
[shadow setShadowBlurRadius:2.5];
- [xPath_ fillWithInnerShadow:shadow];
-}
-
-- (void)commonInit {
- // Set accessibility description.
- NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_CLOSE);
- [[self cell]
- accessibilitySetOverrideValue:description
- forAttribute:NSAccessibilityDescriptionAttribute];
-
- // Add a tooltip.
- [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB)];
-}
-
-- (void)setUpDrawingPaths {
- // Keep the paths centered around the origin in this function. It is then
- // translated in -updatePaths.
- NSPoint xCenter = NSZeroPoint;
-
- circlePath_.reset([[NSBezierPath bezierPath] retain]);
- [circlePath_ moveToPoint:xCenter];
- CGFloat radius = kCircleRadius;
- [circlePath_ appendBezierPathWithArcWithCenter:xCenter
- radius:radius
- startAngle:0.0
- endAngle:365.0];
-
- // Construct an 'x' by drawing two intersecting rectangles in the shape of a
- // cross and then rotating the path by 45 degrees.
- xPath_.reset([[NSBezierPath bezierPath] retain]);
- [xPath_ appendBezierPathWithRect:NSMakeRect(3.5, 7.0, 9.0, 2.0)];
- [xPath_ appendBezierPathWithRect:NSMakeRect(7.0, 3.5, 2.0, 9.0)];
-
- NSRect pathBounds = [xPath_ bounds];
- NSPoint pathCenter = NSMakePoint(NSMidX(pathBounds), NSMidY(pathBounds));
-
- NSAffineTransform* transform = [NSAffineTransform transform];
- [transform translateXBy:xCenter.x yBy:xCenter.y];
- [transform rotateByDegrees:45.0];
- [transform translateXBy:-pathCenter.x yBy:-pathCenter.y];
-
- [xPath_ transformUsingAffineTransform:transform];
-}
-
-- (void)updatePaths {
- oldSize_ = [self bounds].size;
-
- // Revert the current transform for the two points.
- if (transform_.get()) {
- [transform_.get() invert];
- [circlePath_.get() transformUsingAffineTransform:transform_.get()];
- [xPath_.get() transformUsingAffineTransform:transform_.get()];
- }
-
- // Create the new transform. [self bounds] is prefered in case aRect wasn't
- // literally taken as bounds (e.g. cropped).
- NSPoint xCenter = NSMakePoint(8, oldSize_.height / 2.0f);
+ [xPath fillWithInnerShadow:shadow];
- // Retain here, as scoped_* don't retain.
- transform_.reset([[NSAffineTransform transform] retain]);
- [transform_.get() translateXBy:xCenter.x yBy:xCenter.y];
- [circlePath_.get() transformUsingAffineTransform:transform_.get()];
- [xPath_.get() transformUsingAffineTransform:transform_.get()];
+ // CGImage returns an autoreleased CGImageRef.
+ return [imageRep CGImage];
}
@end
« 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