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;
26 const CGFloat kButtonHeight = 28;
27 const CGFloat kButtonPaddingX = 14;
29 ButtonState cellButtonState(id<ConstrainedWindowButtonDrawableCell> cell) {
32 if (![cell isEnabled])
33 return BUTTON_DISABLED;
34 if ([cell isHighlighted])
35 return BUTTON_PRESSED;
36 if ([cell isMouseInside])
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:
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];
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];
78 NSColor* GetButtonBorderColor(ButtonState button_state) {
79 const SkColor color[] = {0x40000000, 0x4D000000, 0x4D000000, 0x1F000000};
81 return gfx::SkColorToCalibratedNSColor(color[button_state]);
84 NSAttributedString* GetButtonAttributedString(
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];
105 if ([key_equivalent isEqualToString:kKeyEquivalentReturn])
106 font = [NSFont boldSystemFontOfSize:12];
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,
117 return [[[NSAttributedString alloc] initWithString:title
118 attributes:attributes] autorelease];
121 void DrawBackgroundAndShadow(const NSRect& frame,
122 id<ConstrainedWindowButtonDrawableCell> cell,
124 NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:frame
127 [ConstrainedWindowButton DrawBackgroundAndShadowForPath:path
132 void DrawInnerHighlight(const NSRect& frame,
133 id<ConstrainedWindowButtonDrawableCell> cell,
136 [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1)
139 [ConstrainedWindowButton DrawInnerHighlightForPath:path
144 void DrawBorder(const NSRect& frame,
145 id<ConstrainedWindowButtonDrawableCell> cell,
148 [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 0.5, 0.5)
151 [ConstrainedWindowButton DrawBorderForPath:path
158 @implementation ConstrainedWindowButton
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;
174 options |= NSTrackingAssumeInside;
176 trackingArea_.reset([[CrTrackingArea alloc]
177 initWithRect:NSZeroRect
181 [self addTrackingArea:trackingArea_.get()];
182 if ([[self cell] isMouseInside] != mouseInView) {
183 [[self cell] setIsMouseInside:mouseInView];
184 [self setNeedsDisplay:YES];
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];
203 NSPoint mousePoint = [window mouseLocationOutsideOfEventStream];
204 mousePoint = [self convertPoint:mousePoint fromView:nil];
205 mouseInView = [self mouse:mousePoint inRect:[self bounds]];
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];
219 + (void)DrawBackgroundAndShadowForPath:(NSBezierPath*)path
220 withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
221 inView:(NSView*)view {
222 ButtonState buttonState = cellButtonState(cell);
224 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
225 [GetButtonShadow(buttonState) set];
226 [[[view window] backgroundColor] set];
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];
244 [GetButtonBorderColor(cellButtonState(cell)) set];
250 @implementation ConstrainedWindowButtonCell
252 @synthesize isMouseInside = isMouseInside_;
254 - (void)drawBezelWithFrame:(NSRect)frame inView:(NSView*)controlView {
255 // Inset to leave room for shadow.
258 // Background and shadow
259 DrawBackgroundAndShadow(frame, self, controlView);
262 DrawInnerHighlight(frame, self, controlView);
265 DrawBorder(frame, self, controlView);
268 - (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView*)controlView {
269 // Inset to leave room for shadow.
271 NSAttributedString* title = GetButtonAttributedString(
272 [self title], [self keyEquivalent], self);
273 [self drawTitle:title withFrame:frame inView:controlView];
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;
285 - (NSAttributedString*)getAttributedTitle {
286 return GetButtonAttributedString(
287 [self title], [self keyEquivalent], self);