blob: 0c8cb0934d6ccd73dea977ab977e790febdfb4aa [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
#include "base/mac/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
#include "skia/ext/skia_utils_mac.h"
#import "third_party/molokocacao/NSBezierPath+MCAdditions.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
@interface ConstrainedWindowButton ()
- (BOOL)isMouseReallyInside;
@end
namespace {
enum ButtonState {
BUTTON_NORMAL,
BUTTON_HOVER,
BUTTON_PRESSED,
BUTTON_DISABLED,
};
const CGFloat kButtonHeight = 28;
const CGFloat kButtonPaddingX = 14;
ButtonState cellButtonState(id<ConstrainedWindowButtonDrawableCell> cell) {
if (!cell)
return BUTTON_NORMAL;
if (![cell isEnabled])
return BUTTON_DISABLED;
if ([cell isHighlighted])
return BUTTON_PRESSED;
if ([cell isMouseInside])
return BUTTON_HOVER;
return BUTTON_NORMAL;
}
// The functions below use hex color values to make it easier to compare
// the code with the spec. Table of hex values are also more readable
// then tables of NSColor constructors.
NSGradient* GetButtonGradient(ButtonState button_state) {
const SkColor start[] = {0xFFF0F0F0, 0xFFF4F4F4, 0xFFEBEBEB, 0xFFEDEDED};
const SkColor end[] = {0xFFE0E0E0, 0xFFE4E4E4, 0xFFDBDBDB, 0xFFDEDEDE};
NSColor* start_color = gfx::SkColorToCalibratedNSColor(start[button_state]);
NSColor* end_color = gfx::SkColorToCalibratedNSColor(end[button_state]);
return [[[NSGradient alloc] initWithColorsAndLocations:
start_color, 0.0,
start_color, 0.38,
end_color, 1.0,
nil] autorelease];
}
NSShadow* GetButtonHighlight(ButtonState button_state) {
const SkColor color[] = {0xBFFFFFFF, 0xF2FFFFFF, 0x24000000, 0x00000000};
NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
[shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
[shadow setShadowOffset:NSMakeSize(0, -1)];
[shadow setShadowBlurRadius:2];
return shadow;
}
NSShadow* GetButtonShadow(ButtonState button_state) {
const SkColor color[] = {0x14000000, 0x1F000000, 0x00000000, 0x00000000};
NSShadow* shadow = [[[NSShadow alloc] init] autorelease];
[shadow setShadowColor:gfx::SkColorToCalibratedNSColor(color[button_state])];
[shadow setShadowOffset:NSMakeSize(0, -1)];
[shadow setShadowBlurRadius:0];
return shadow;
}
NSColor* GetButtonBorderColor(ButtonState button_state) {
const SkColor color[] = {0x40000000, 0x4D000000, 0x4D000000, 0x1F000000};
return gfx::SkColorToCalibratedNSColor(color[button_state]);
}
NSAttributedString* GetButtonAttributedString(
NSString* title,
NSString* key_equivalent,
id<ConstrainedWindowButtonDrawableCell> cell) {
const SkColor text_color[] = {0xFF333333, 0XFF000000, 0xFF000000, 0xFFAAAAAA};
// The shadow color should be 0xFFF0F0F0 but that doesn't show up so use
// pure white instead.
const SkColor shadow_color[] =
{0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF};
base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
[shadow setShadowColor:
gfx::SkColorToCalibratedNSColor(shadow_color[cellButtonState(cell)])];
[shadow setShadowOffset:NSMakeSize(0, -1)];
[shadow setShadowBlurRadius:0];
base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
[[NSMutableParagraphStyle alloc] init]);
[paragraphStyle setAlignment:NSCenterTextAlignment];
NSFont* font = nil;
if ([key_equivalent isEqualToString:kKeyEquivalentReturn])
font = [NSFont boldSystemFontOfSize:12];
else
font = [NSFont systemFontOfSize:12];
NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
gfx::SkColorToCalibratedNSColor(text_color[cellButtonState(cell)]),
NSForegroundColorAttributeName,
shadow.get(), NSShadowAttributeName,
paragraphStyle.get(), NSParagraphStyleAttributeName,
nil];
return [[[NSAttributedString alloc] initWithString:title
attributes:attributes] autorelease];
}
void DrawBackgroundAndShadow(const NSRect& frame,
id<ConstrainedWindowButtonDrawableCell> cell,
NSView* view) {
NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:frame
xRadius:2
yRadius:2];
[ConstrainedWindowButton DrawBackgroundAndShadowForPath:path
withCell:cell
inView:view];
}
void DrawInnerHighlight(const NSRect& frame,
id<ConstrainedWindowButtonDrawableCell> cell,
NSView* view) {
NSBezierPath* path =
[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1)
xRadius:2
yRadius:2];
[ConstrainedWindowButton DrawInnerHighlightForPath:path
withCell:cell
inView:view];
}
void DrawBorder(const NSRect& frame,
id<ConstrainedWindowButtonDrawableCell> cell,
NSView* view) {
NSBezierPath* path =
[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 0.5, 0.5)
xRadius:2
yRadius:2];
[ConstrainedWindowButton DrawBorderForPath:path
withCell:cell
inView:view];
}
} // namespace
@implementation ConstrainedWindowButton
+ (Class)cellClass {
return [ConstrainedWindowButtonCell class];
}
- (void)updateTrackingAreas {
BOOL mouseInView = [self isMouseReallyInside];
if (trackingArea_.get())
[self removeTrackingArea:trackingArea_.get()];
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
NSTrackingActiveInActiveApp |
NSTrackingInVisibleRect;
if (mouseInView)
options |= NSTrackingAssumeInside;
trackingArea_.reset([[CrTrackingArea alloc]
initWithRect:NSZeroRect
options:options
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_.get()];
if ([[self cell] isMouseInside] != mouseInView) {
[[self cell] setIsMouseInside:mouseInView];
[self setNeedsDisplay:YES];
}
}
- (void)mouseEntered:(NSEvent*)theEvent {
[[self cell] setIsMouseInside:YES];
[self setNeedsDisplay:YES];
}
- (void)mouseExited:(NSEvent*)theEvent {
[[self cell] setIsMouseInside:YES];
[[self cell] setIsMouseInside:NO];
[self setNeedsDisplay:YES];
}
- (BOOL)isMouseReallyInside {
BOOL mouseInView = NO;
NSWindow* window = [self window];
if (window) {
NSPoint mousePoint = [window mouseLocationOutsideOfEventStream];
mousePoint = [self convertPoint:mousePoint fromView:nil];
mouseInView = [self mouse:mousePoint inRect:[self bounds]];
}
return mouseInView;
}
- (void)sizeToFit {
[super sizeToFit];
NSSize size = [self frame].size;
if (size.width < constrained_window_button::kButtonMinWidth) {
size.width = constrained_window_button::kButtonMinWidth;
[self setFrameSize:size];
}
}
+ (void)DrawBackgroundAndShadowForPath:(NSBezierPath*)path
withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
inView:(NSView*)view {
ButtonState buttonState = cellButtonState(cell);
{
gfx::ScopedNSGraphicsContextSaveGState scopedGState;
[GetButtonShadow(buttonState) set];
[[[view window] backgroundColor] set];
[path fill];
}
[GetButtonGradient(buttonState) drawInBezierPath:path angle:90.0];
}
+ (void)DrawInnerHighlightForPath:(NSBezierPath*)path
withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
inView:(NSView*)view {
[path fillWithInnerShadow:GetButtonHighlight(cellButtonState(cell))];
}
+ (void)DrawBorderForPath:(NSBezierPath*)path
withCell:(id<ConstrainedWindowButtonDrawableCell>)cell
inView:(NSView*)view {
if ([[[view window] firstResponder] isEqual:view])
[[NSColor colorWithCalibratedRed:0.30 green:0.57 blue:1.0 alpha:1.0] set];
else
[GetButtonBorderColor(cellButtonState(cell)) set];
[path stroke];
}
@end
@implementation ConstrainedWindowButtonCell
@synthesize isMouseInside = isMouseInside_;
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView*)controlView {
// Inset to leave room for shadow.
--frame.size.height;
// Background and shadow
DrawBackgroundAndShadow(frame, self, controlView);
// Inner highlight
DrawInnerHighlight(frame, self, controlView);
// Border
DrawBorder(frame, self, controlView);
}
- (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView*)controlView {
// Inset to leave room for shadow.
--frame.size.height;
NSAttributedString* title = GetButtonAttributedString(
[self title], [self keyEquivalent], self);
[self drawTitle:title withFrame:frame inView:controlView];
}
- (NSSize)cellSize {
NSAttributedString* title = GetButtonAttributedString(
[self title], [self keyEquivalent], self);
NSSize size = [title size];
size.height = std::max(size.height, kButtonHeight);
size.width += kButtonPaddingX * 2;
return size;
}
- (NSAttributedString*)getAttributedTitle {
return GetButtonAttributedString(
[self title], [self keyEquivalent], self);
}
@end