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 3// found in the LICENSE file. 4 5#import "chrome/browser/ui/cocoa/hover_close_button.h" 6 7#include "base/memory/scoped_nsobject.h" 8#include "grit/generated_resources.h" 9#import "third_party/molokocacao/NSBezierPath+MCAdditions.h" 10#include "ui/base/l10n/l10n_util.h" 11 12namespace { 13const CGFloat kCircleRadius = 0.415 * 16; 14const CGFloat kCircleHoverWhite = 0.565; 15const CGFloat kCircleClickWhite = 0.396; 16const CGFloat kXShadowAlpha = 0.75; 17const CGFloat kXShadowCircleAlpha = 0.1; 18} // namespace 19 20@interface HoverCloseButton(Private) 21- (void)updatePaths; 22- (void)setUpDrawingPaths; 23@end 24 25@implementation HoverCloseButton 26 27- (id)initWithFrame:(NSRect)frameRect { 28 if ((self = [super initWithFrame:frameRect])) { 29 [self commonInit]; 30 } 31 return self; 32} 33 34- (void)awakeFromNib { 35 [super awakeFromNib]; 36 [self commonInit]; 37} 38 39- (void)drawRect:(NSRect)rect { 40 if (!circlePath_.get() || !xPath_.get()) 41 [self setUpDrawingPaths]; 42 43 // Only call updatePaths if the size changed. 44 if (!NSEqualSizes(oldSize_, [self bounds].size)) 45 [self updatePaths]; 46 47 // If the user is hovering over the button, a light/dark gray circle is drawn 48 // behind the 'x'. 49 if (hoverState_ != kHoverStateNone) { 50 // Adjust the darkness of the circle depending on whether it is being 51 // clicked. 52 CGFloat white = (hoverState_ == kHoverStateMouseOver) ? 53 kCircleHoverWhite : kCircleClickWhite; 54 [[NSColor colorWithCalibratedWhite:white alpha:1.0] set]; 55 [circlePath_ fill]; 56 } 57 58 [[NSColor whiteColor] set]; 59 [xPath_ fill]; 60 61 // 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). 63 NSShadow* shadow = [[[NSShadow alloc] init] autorelease]; 64 CGFloat alpha = (hoverState_ != kHoverStateNone) ? 65 kXShadowCircleAlpha : kXShadowAlpha; 66 [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.15 67 alpha:alpha]]; 68 [shadow setShadowOffset:NSMakeSize(0.0, 0.0)]; 69 [shadow setShadowBlurRadius:2.5]; 70 [xPath_ fillWithInnerShadow:shadow]; 71} 72 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} 134 135@end 136