• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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