Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_button.mm
blob0c8cb0934d6ccd73dea977ab977e790febdfb4aa
1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
7 #include "base/mac/scoped_nsobject.h"
8 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
9 #include "skia/ext/skia_utils_mac.h"
10 #import "third_party/molokocacao/NSBezierPath+MCAdditions.h"
11 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
13 @interface ConstrainedWindowButton ()
14 - (BOOL)isMouseReallyInside;
15 @end
17 namespace {
19 enum ButtonState {
20   BUTTON_NORMAL,
21   BUTTON_HOVER,
22   BUTTON_PRESSED,
23   BUTTON_DISABLED,
26 const CGFloat kButtonHeight = 28;
27 const CGFloat kButtonPaddingX = 14;
29 ButtonState cellButtonState(id<ConstrainedWindowButtonDrawableCell> cell) {
30   if (!cell)
31     return BUTTON_NORMAL;
32   if (![cell isEnabled])
33     return BUTTON_DISABLED;
34   if ([cell isHighlighted])
35     return BUTTON_PRESSED;
36   if ([cell isMouseInside])
37     return BUTTON_HOVER;
38   return BUTTON_NORMAL;
41 // The functions below use hex color values to make it easier to compare
42 // the code with the spec. Table of hex values are also more readable
43 // then tables of NSColor constructors.
45 NSGradient* GetButtonGradient(ButtonState button_state) {
46   const SkColor start[] = {0xFFF0F0F0, 0xFFF4F4F4, 0xFFEBEBEB, 0xFFEDEDED};
47   const SkColor end[] = {0xFFE0E0E0, 0xFFE4E4E4, 0xFFDBDBDB, 0xFFDEDEDE};
49   NSColor* start_color = gfx::SkColorToCalibratedNSColor(start[button_state]);
50   NSColor* end_color = gfx::SkColorToCalibratedNSColor(end[button_state]);
51   return [[[NSGradient alloc] initWithColorsAndLocations:
52       start_color, 0.0,
53       start_color, 0.38,
54       end_color, 1.0,
55       nil] autorelease];
58 NSShadow* GetButtonHighlight(ButtonState button_state) {
59   const SkColor color[] = {0xBFFFFFFF, 0xF2FFFFFF, 0x24000000, 0x00000000};
61   NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
62   [shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
63   [shadow setShadowOffset:NSMakeSize(0, -1)];
64   [shadow setShadowBlurRadius:2];
65   return shadow;
68 NSShadow* GetButtonShadow(ButtonState button_state) {
69   const SkColor color[] = {0x14000000, 0x1F000000, 0x00000000, 0x00000000};
71   NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
72   [shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
73   [shadow setShadowOffset:NSMakeSize(0, -1)];
74   [shadow setShadowBlurRadius:0];
75   return shadow;
78 NSColor* GetButtonBorderColor(ButtonState button_state) {
79   const SkColor color[] = {0x40000000, 0x4D000000, 0x4D000000, 0x1F000000};
81   return gfx::SkColorToCalibratedNSColor(color[button_state]);
84 NSAttributedString* GetButtonAttributedString(
85     NSString* title,
86     NSString* key_equivalent,
87     id<ConstrainedWindowButtonDrawableCell> cell) {
88   const SkColor text_color[] = {0xFF333333, 0XFF000000, 0xFF000000, 0xFFAAAAAA};
89   // The shadow color should be 0xFFF0F0F0 but that doesn't show up so use
90   // pure white instead.
91   const SkColor shadow_color[] =
92       {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF};
94   base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
95   [shadow setShadowColor:
96       gfx::SkColorToCalibratedNSColor(shadow_color[cellButtonState(cell)])];
97   [shadow setShadowOffset:NSMakeSize(0, -1)];
98   [shadow setShadowBlurRadius:0];
100   base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
101       [[NSMutableParagraphStyle alloc] init]);
102   [paragraphStyle setAlignment:NSCenterTextAlignment];
104   NSFont* font = nil;
105   if ([key_equivalent isEqualToString:kKeyEquivalentReturn])
106     font = [NSFont boldSystemFontOfSize:12];
107   else
108     font = [NSFont systemFontOfSize:12];
110   NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
111       font, NSFontAttributeName,
112       gfx::SkColorToCalibratedNSColor(text_color[cellButtonState(cell)]),
113       NSForegroundColorAttributeName,
114       shadow.get(), NSShadowAttributeName,
115       paragraphStyle.get(), NSParagraphStyleAttributeName,
116       nil];
117   return [[[NSAttributedString alloc] initWithString:title
118                                           attributes:attributes] autorelease];
121 void DrawBackgroundAndShadow(const NSRect& frame,
122                              id<ConstrainedWindowButtonDrawableCell> cell,
123                              NSView* view) {
124   NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:frame
125                                                        xRadius:2
126                                                        yRadius:2];
127   [ConstrainedWindowButton DrawBackgroundAndShadowForPath:path
128                                                  withCell:cell
129                                                    inView:view];
132 void DrawInnerHighlight(const NSRect& frame,
133                         id<ConstrainedWindowButtonDrawableCell> cell,
134                         NSView* view) {
135   NSBezierPath* path =
136       [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1)
137                                       xRadius:2
138                                       yRadius:2];
139   [ConstrainedWindowButton DrawInnerHighlightForPath:path
140                                             withCell:cell
141                                               inView:view];
144 void DrawBorder(const NSRect& frame,
145                 id<ConstrainedWindowButtonDrawableCell> cell,
146                 NSView* view) {
147   NSBezierPath* path =
148       [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 0.5, 0.5)
149                                       xRadius:2
150                                       yRadius:2];
151   [ConstrainedWindowButton DrawBorderForPath:path
152                                     withCell:cell
153                                       inView:view];
156 }  // namespace
158 @implementation ConstrainedWindowButton
160 + (Class)cellClass {
161   return [ConstrainedWindowButtonCell class];
164 - (void)updateTrackingAreas {
165   BOOL mouseInView = [self isMouseReallyInside];
167   if (trackingArea_.get())
168     [self removeTrackingArea:trackingArea_.get()];
170   NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
171                                   NSTrackingActiveInActiveApp |
172                                   NSTrackingInVisibleRect;
173   if (mouseInView)
174     options |= NSTrackingAssumeInside;
176   trackingArea_.reset([[CrTrackingArea alloc]
177                         initWithRect:NSZeroRect
178                              options:options
179                                owner:self
180                             userInfo:nil]);
181   [self addTrackingArea:trackingArea_.get()];
182   if ([[self cell] isMouseInside] != mouseInView) {
183     [[self cell] setIsMouseInside:mouseInView];
184     [self setNeedsDisplay:YES];
185   }
188 - (void)mouseEntered:(NSEvent*)theEvent {
189   [[self cell] setIsMouseInside:YES];
190   [self setNeedsDisplay:YES];
193 - (void)mouseExited:(NSEvent*)theEvent {
194   [[self cell] setIsMouseInside:YES];
195   [[self cell] setIsMouseInside:NO];
196   [self setNeedsDisplay:YES];
199 - (BOOL)isMouseReallyInside {
200   BOOL mouseInView = NO;
201   NSWindow* window = [self window];
202   if (window) {
203     NSPoint mousePoint = [window mouseLocationOutsideOfEventStream];
204     mousePoint = [self convertPoint:mousePoint fromView:nil];
205     mouseInView = [self mouse:mousePoint inRect:[self bounds]];
206   }
207   return mouseInView;
210 - (void)sizeToFit {
211   [super sizeToFit];
212   NSSize size = [self frame].size;
213   if (size.width < constrained_window_button::kButtonMinWidth) {
214     size.width = constrained_window_button::kButtonMinWidth;
215     [self setFrameSize:size];
216   }
219 + (void)DrawBackgroundAndShadowForPath:(NSBezierPath*)path
220                           withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
221                             inView:(NSView*)view {
222   ButtonState buttonState = cellButtonState(cell);
223   {
224     gfx::ScopedNSGraphicsContextSaveGState scopedGState;
225     [GetButtonShadow(buttonState) set];
226     [[[view window] backgroundColor] set];
227     [path fill];
228   }
229   [GetButtonGradient(buttonState) drawInBezierPath:path angle:90.0];
232 + (void)DrawInnerHighlightForPath:(NSBezierPath*)path
233                          withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
234                            inView:(NSView*)view {
235   [path fillWithInnerShadow:GetButtonHighlight(cellButtonState(cell))];
238 + (void)DrawBorderForPath:(NSBezierPath*)path
239                  withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
240                    inView:(NSView*)view {
241   if ([[[view window] firstResponder] isEqual:view])
242     [[NSColor colorWithCalibratedRed:0.30 green:0.57 blue:1.0 alpha:1.0] set];
243   else
244     [GetButtonBorderColor(cellButtonState(cell)) set];
245   [path stroke];
248 @end
250 @implementation ConstrainedWindowButtonCell
252 @synthesize isMouseInside = isMouseInside_;
254 - (void)drawBezelWithFrame:(NSRect)frame inView:(NSView*)controlView {
255   // Inset to leave room for shadow.
256   --frame.size.height;
258   // Background and shadow
259   DrawBackgroundAndShadow(frame, self, controlView);
261   // Inner highlight
262   DrawInnerHighlight(frame, self, controlView);
264   // Border
265   DrawBorder(frame, self, controlView);
268 - (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView*)controlView {
269   // Inset to leave room for shadow.
270   --frame.size.height;
271   NSAttributedString* title = GetButtonAttributedString(
272      [self title], [self keyEquivalent], self);
273   [self drawTitle:title withFrame:frame inView:controlView];
276 - (NSSize)cellSize {
277   NSAttributedString* title = GetButtonAttributedString(
278       [self title], [self keyEquivalent], self);
279   NSSize size = [title size];
280   size.height = std::max(size.height, kButtonHeight);
281   size.width += kButtonPaddingX * 2;
282   return size;
285 - (NSAttributedString*)getAttributedTitle {
286   return GetButtonAttributedString(
287       [self title], [self keyEquivalent], self);
290 @end