Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_button.mm
blobeed5dbbe3bd198cd2dfe78349cb3e6d1a6c3481a
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 namespace {
15 enum ButtonState {
16   BUTTON_NORMAL,
17   BUTTON_HOVER,
18   BUTTON_PRESSED,
19   BUTTON_DISABLED,
22 const CGFloat kButtonMinWidth = 72;
24 }  // namespace
26 @interface ConstrainedWindowButton ()
27 - (BOOL)isMouseReallyInside;
28 @end
30 @interface ConstrainedWindowButtonCell ()
31 - (ButtonState)buttonState;
32 @end
34 namespace {
36 const CGFloat kButtonHeight = 28;
37 const CGFloat kButtonPaddingX = 14;
39 // The functions below use hex color values to make it easier to compare
40 // the code with the spec. Table of hex values are also more readable
41 // then tables of NSColor constructors.
43 NSGradient* GetButtonGradient(ButtonState button_state) {
44   const SkColor start[] = {0xFFF0F0F0, 0xFFF4F4F4, 0xFFEBEBEB, 0xFFEDEDED};
45   const SkColor end[] = {0xFFE0E0E0, 0xFFE4E4E4, 0xFFDBDBDB, 0xFFDEDEDE};
47   NSColor* start_color = gfx::SkColorToCalibratedNSColor(start[button_state]);
48   NSColor* end_color = gfx::SkColorToCalibratedNSColor(end[button_state]);
49   return [[[NSGradient alloc] initWithColorsAndLocations:
50       start_color, 0.0,
51       start_color, 0.38,
52       end_color, 1.0,
53       nil] autorelease];
56 NSShadow* GetButtonHighlight(ButtonState button_state) {
57   const SkColor color[] = {0xBFFFFFFF, 0xF2FFFFFF, 0x24000000, 0x00000000};
59   NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
60   [shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
61   [shadow setShadowOffset:NSMakeSize(0, -1)];
62   [shadow setShadowBlurRadius:2];
63   return shadow;
66 NSShadow* GetButtonShadow(ButtonState button_state) {
67   const SkColor color[] = {0x14000000, 0x1F000000, 0x00000000, 0x00000000};
69   NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
70   [shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
71   [shadow setShadowOffset:NSMakeSize(0, -1)];
72   [shadow setShadowBlurRadius:0];
73   return shadow;
76 NSColor* GetButtonBorderColor(ButtonState button_state) {
77   const SkColor color[] = {0x40000000, 0x4D000000, 0x4D000000, 0x1F000000};
79   return gfx::SkColorToCalibratedNSColor(color[button_state]);
82 NSAttributedString* GetButtonAttributedString(NSString* title,
83                                               NSString* key_equivalent,
84                                               ButtonState button_state) {
85   const SkColor text_color[] = {0xFF333333, 0XFF000000, 0xFF000000, 0xFFAAAAAA};
86   // The shadow color should be 0xFFF0F0F0 but that doesn't show up so use
87   // pure white instead.
88   const SkColor shadow_color[] =
89       {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF};
91   base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
92   [shadow setShadowColor:
93       gfx::SkColorToCalibratedNSColor(shadow_color[button_state])];
94   [shadow setShadowOffset:NSMakeSize(0, -1)];
95   [shadow setShadowBlurRadius:0];
97   base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
98       [[NSMutableParagraphStyle alloc] init]);
99   [paragraphStyle setAlignment:NSCenterTextAlignment];
101   NSFont* font = nil;
102   if ([key_equivalent isEqualToString:kKeyEquivalentReturn])
103     font = [NSFont boldSystemFontOfSize:12];
104   else
105     font = [NSFont systemFontOfSize:12];
107   NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
108       font, NSFontAttributeName,
109       gfx::SkColorToCalibratedNSColor(text_color[button_state]),
110       NSForegroundColorAttributeName,
111       shadow.get(), NSShadowAttributeName,
112       paragraphStyle.get(), NSParagraphStyleAttributeName,
113       nil];
114   return [[[NSAttributedString alloc] initWithString:title
115                                           attributes:attributes] autorelease];
118 }  // namespace
120 @implementation ConstrainedWindowButton
122 + (Class)cellClass {
123   return [ConstrainedWindowButtonCell class];
126 - (void)updateTrackingAreas {
127   BOOL mouseInView = [self isMouseReallyInside];
129   if (trackingArea_.get())
130     [self removeTrackingArea:trackingArea_.get()];
132   NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
133                                   NSTrackingActiveInActiveApp |
134                                   NSTrackingInVisibleRect;
135   if (mouseInView)
136     options |= NSTrackingAssumeInside;
138   trackingArea_.reset([[CrTrackingArea alloc]
139                         initWithRect:NSZeroRect
140                              options:options
141                                owner:self
142                             userInfo:nil]);
143   [self addTrackingArea:trackingArea_.get()];
144   if ([[self cell] isMouseInside] != mouseInView) {
145     [[self cell] setIsMouseInside:mouseInView];
146     [self setNeedsDisplay:YES];
147   }
150 - (void)mouseEntered:(NSEvent*)theEvent {
151   [[self cell] setIsMouseInside:YES];
152   [self setNeedsDisplay:YES];
155 - (void)mouseExited:(NSEvent*)theEvent {
156   [[self cell] setIsMouseInside:YES];
157   [[self cell] setIsMouseInside:NO];
158   [self setNeedsDisplay:YES];
161 - (BOOL)isMouseReallyInside {
162   BOOL mouseInView = NO;
163   NSWindow* window = [self window];
164   if (window) {
165     NSPoint mousePoint = [window mouseLocationOutsideOfEventStream];
166     mousePoint = [self convertPoint:mousePoint fromView:nil];
167     mouseInView = [self mouse:mousePoint inRect:[self bounds]];
168   }
169   return mouseInView;
172 - (void)sizeToFit {
173   [super sizeToFit];
174   NSSize size = [self frame].size;
175   if (size.width < kButtonMinWidth) {
176     size.width = kButtonMinWidth;
177     [self setFrameSize:size];
178   }
181 @end
183 @implementation ConstrainedWindowButtonCell
185 @synthesize isMouseInside = isMouseInside_;
187 - (void)drawWithFrame:(NSRect)frame
188                inView:(NSView *)controlView {
189   NSBezierPath* path = nil;
190   ButtonState buttonState = [self buttonState];
192   // Inset to leave room for shadow.
193   --frame.size.height;
195   // Background and shadow
196   path = [NSBezierPath bezierPathWithRoundedRect:frame
197                                          xRadius:2
198                                          yRadius:2];
199   {
200     gfx::ScopedNSGraphicsContextSaveGState scopedGState;
201     [GetButtonShadow(buttonState) set];
202     [[[controlView window] backgroundColor] set];
203     [path fill];
204   }
205   [GetButtonGradient(buttonState) drawInBezierPath:path angle:90.0];
207   // Inner highlight
208   path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1)
209                                          xRadius:2
210                                          yRadius:2];
211   [path fillWithInnerShadow:GetButtonHighlight(buttonState)];
213   // Border
214   path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 0.5, 0.5)
215                                          xRadius:2
216                                          yRadius:2];
217   if ([[[controlView window] firstResponder] isEqual:controlView])
218     [[NSColor colorWithCalibratedRed:0.30 green:0.57 blue:1.0 alpha:1.0] set];
219   else
220     [GetButtonBorderColor(buttonState) set];
221   [path stroke];
223   NSAttributedString* title = GetButtonAttributedString(
224       [self title], [self keyEquivalent], buttonState);
225   [self drawTitle:title withFrame:frame inView:controlView];
228 - (NSSize)cellSize {
229   NSAttributedString* title = GetButtonAttributedString(
230       [self title], [self keyEquivalent], [self buttonState]);
231   NSSize size = [title size];
232   size.height = std::max(size.height, kButtonHeight);
233   size.width += kButtonPaddingX * 2;
234   return size;
237 - (ButtonState)buttonState {
238   if (![self isEnabled])
239     return BUTTON_DISABLED;
240   if ([self isHighlighted])
241     return BUTTON_PRESSED;
242   if (isMouseInside_)
243     return BUTTON_HOVER;
244   return BUTTON_NORMAL;
247 @end