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"
22 const CGFloat kButtonMinWidth = 72;
26 @interface ConstrainedWindowButton ()
27 - (BOOL)isMouseReallyInside;
30 @interface ConstrainedWindowButtonCell ()
31 - (ButtonState)buttonState;
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:
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];
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];
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];
102 if ([key_equivalent isEqualToString:kKeyEquivalentReturn])
103 font = [NSFont boldSystemFontOfSize:12];
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,
114 return [[[NSAttributedString alloc] initWithString:title
115 attributes:attributes] autorelease];
120 @implementation ConstrainedWindowButton
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;
136 options |= NSTrackingAssumeInside;
138 trackingArea_.reset([[CrTrackingArea alloc]
139 initWithRect:NSZeroRect
143 [self addTrackingArea:trackingArea_.get()];
144 if ([[self cell] isMouseInside] != mouseInView) {
145 [[self cell] setIsMouseInside:mouseInView];
146 [self setNeedsDisplay:YES];
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];
165 NSPoint mousePoint = [window mouseLocationOutsideOfEventStream];
166 mousePoint = [self convertPoint:mousePoint fromView:nil];
167 mouseInView = [self mouse:mousePoint inRect:[self bounds]];
174 NSSize size = [self frame].size;
175 if (size.width < kButtonMinWidth) {
176 size.width = kButtonMinWidth;
177 [self setFrameSize:size];
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.
195 // Background and shadow
196 path = [NSBezierPath bezierPathWithRoundedRect:frame
200 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
201 [GetButtonShadow(buttonState) set];
202 [[[controlView window] backgroundColor] set];
205 [GetButtonGradient(buttonState) drawInBezierPath:path angle:90.0];
208 path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1)
211 [path fillWithInnerShadow:GetButtonHighlight(buttonState)];
214 path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 0.5, 0.5)
217 if ([[[controlView window] firstResponder] isEqual:controlView])
218 [[NSColor colorWithCalibratedRed:0.30 green:0.57 blue:1.0 alpha:1.0] set];
220 [GetButtonBorderColor(buttonState) set];
223 NSAttributedString* title = GetButtonAttributedString(
224 [self title], [self keyEquivalent], buttonState);
225 [self drawTitle:title withFrame:frame inView:controlView];
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;
237 - (ButtonState)buttonState {
238 if (![self isEnabled])
239 return BUTTON_DISABLED;
240 if ([self isHighlighted])
241 return BUTTON_PRESSED;
244 return BUTTON_NORMAL;