blob: 773c91d6c8963043d1477df5381f4a29f457ec5c [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2011 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.
4
5#import "chrome/browser/ui/cocoa/image_button_cell.h"
6
7#include "base/logging.h"
8#import "chrome/browser/themes/theme_service.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00009#import "chrome/browser/ui/cocoa/nsview_additions.h"
10#import "chrome/browser/ui/cocoa/rect_path_utils.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011#import "chrome/browser/ui/cocoa/themed_window.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012#include "ui/base/resource/resource_bundle.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000013#include "ui/gfx/image/image.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016// When the window doesn't have focus then we want to draw the button with a
17// slightly lighter color. We do this by just reducing the alpha.
18const CGFloat kImageNoFocusAlpha = 0.65;
19
Torne (Richard Coles)58218062012-11-14 11:43:16 +000020@interface ImageButtonCell (Private)
21- (void)sharedInit;
22- (image_button_cell::ButtonState)currentButtonState;
23- (NSImage*)imageForID:(NSInteger)imageID
24 controlView:(NSView*)controlView;
25@end
26
27@implementation ImageButtonCell
28
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000029@synthesize isMouseInside = isMouseInside_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030
31// For nib instantiations
32- (id)initWithCoder:(NSCoder*)decoder {
33 if ((self = [super initWithCoder:decoder])) {
34 [self sharedInit];
35 }
36 return self;
37}
38
39// For programmatic instantiations
40- (id)initTextCell:(NSString*)string {
41 if ((self = [super initTextCell:string])) {
42 [self sharedInit];
43 }
44 return self;
45}
46
47- (void)sharedInit {
48 [self setHighlightsBy:NSNoCellMask];
49
50 // We need to set this so that we can override |-mouseEntered:| and
51 // |-mouseExited:| to change the button image on hover states.
52 [self setShowsBorderOnlyWhileMouseInside:YES];
53}
54
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000055- (NSImage*)imageForState:(image_button_cell::ButtonState)state
56 view:(NSView*)controlView{
57 if (image_[state].imageId)
58 return [self imageForID:image_[state].imageId controlView:controlView];
59 return image_[state].image;
60}
61
Torne (Richard Coles)58218062012-11-14 11:43:16 +000062- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000063 image_button_cell::ButtonState state = [self currentButtonState];
Torne (Richard Coles)58218062012-11-14 11:43:16 +000064 BOOL windowHasFocus = [[controlView window] isMainWindow] ||
65 [[controlView window] isKeyWindow];
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010066 CGFloat alpha = [self imageAlphaForWindowState:[controlView window]];
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000067 NSImage* image = [self imageForState:state view:controlView];
Torne (Richard Coles)58218062012-11-14 11:43:16 +000068
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000069 if (!windowHasFocus) {
70 NSImage* defaultImage = [self
71 imageForState:image_button_cell::kDefaultStateBackground
72 view:controlView];
73 NSImage* hoverImage = [self
74 imageForState:image_button_cell::kHoverStateBackground
75 view:controlView];
76 if ([self currentButtonState] == image_button_cell::kDefaultState &&
77 defaultImage) {
78 image = defaultImage;
79 alpha = 1.0;
80 } else if ([self currentButtonState] == image_button_cell::kHoverState &&
81 hoverImage) {
82 image = hoverImage;
83 alpha = 1.0;
84 }
85 }
86
Torne (Richard Coles)58218062012-11-14 11:43:16 +000087 NSRect imageRect;
88 imageRect.size = [image size];
89 imageRect.origin.x = cellFrame.origin.x +
90 roundf((NSWidth(cellFrame) - NSWidth(imageRect)) / 2.0);
91 imageRect.origin.y = cellFrame.origin.y +
92 roundf((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
93
94 [image drawInRect:imageRect
95 fromRect:NSZeroRect
96 operation:NSCompositeSourceOver
97 fraction:alpha
98 respectFlipped:YES
99 hints:nil];
100
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100101 [self drawFocusRingWithFrame:cellFrame inView:controlView];
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000102}
103
104- (void)setImageID:(NSInteger)imageID
105 forButtonState:(image_button_cell::ButtonState)state {
106 DCHECK_GE(state, 0);
107 DCHECK_LT(state, image_button_cell::kButtonStateCount);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000108
109 image_[state].image.reset();
110 image_[state].imageId = imageID;
111 [[self controlView] setNeedsDisplay:YES];
112}
113
114// Sets the image for the given button state using an image.
115- (void)setImage:(NSImage*)image
116 forButtonState:(image_button_cell::ButtonState)state {
117 DCHECK_GE(state, 0);
118 DCHECK_LT(state, image_button_cell::kButtonStateCount);
119
120 image_[state].image.reset([image retain]);
121 image_[state].imageId = 0;
122 [[self controlView] setNeedsDisplay:YES];
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000123}
124
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100125- (CGFloat)imageAlphaForWindowState:(NSWindow*)window {
126 BOOL windowHasFocus = [window isMainWindow] || [window isKeyWindow];
127 return windowHasFocus ? 1.0 : kImageNoFocusAlpha;
128}
129
130- (void)drawFocusRingWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
131 if (![self showsFirstResponder])
132 return;
133 gfx::ScopedNSGraphicsContextSaveGState scoped_state;
134 const CGFloat lineWidth = [controlView cr_lineWidth];
135 rect_path_utils::FrameRectWithInset(rect_path_utils::RoundedCornerAll,
136 NSInsetRect(cellFrame, 0, lineWidth),
137 0.0, // insetX
138 0.0, // insetY
139 3.0, // outerRadius
140 lineWidth * 2, // lineWidth
141 [controlView
142 cr_keyboardFocusIndicatorColor]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000143}
144
145- (image_button_cell::ButtonState)currentButtonState {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000146 bool (^has)(image_button_cell::ButtonState) =
147 ^(image_button_cell::ButtonState state) {
148 return image_[state].image || image_[state].imageId;
149 };
150 if (![self isEnabled] && has(image_button_cell::kDisabledState))
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000151 return image_button_cell::kDisabledState;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000152 if ([self isHighlighted] && has(image_button_cell::kPressedState))
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000153 return image_button_cell::kPressedState;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000154 if ([self isMouseInside] && has(image_button_cell::kHoverState))
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000155 return image_button_cell::kHoverState;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000156 return image_button_cell::kDefaultState;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000157}
158
159- (NSImage*)imageForID:(NSInteger)imageID
160 controlView:(NSView*)controlView {
161 if (!imageID)
162 return nil;
163
164 ui::ThemeProvider* themeProvider = [[controlView window] themeProvider];
165 if (!themeProvider)
166 return nil;
167
Ben Murdochbb1529c2013-08-08 10:24:53 +0100168 return themeProvider->GetNSImageNamed(imageID);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000169}
170
171- (void)setIsMouseInside:(BOOL)isMouseInside {
172 if (isMouseInside_ != isMouseInside) {
173 isMouseInside_ = isMouseInside;
174 NSView<ImageButton>* control =
175 static_cast<NSView<ImageButton>*>([self controlView]);
176 if ([control respondsToSelector:@selector(mouseInsideStateDidChange:)]) {
177 [control mouseInsideStateDidChange:isMouseInside];
178 }
179 [control setNeedsDisplay:YES];
180 }
181}
182
183- (void)setShowsBorderOnlyWhileMouseInside:(BOOL)show {
184 VLOG_IF(1, !show) << "setShowsBorderOnlyWhileMouseInside:NO ignored";
185}
186
187- (BOOL)showsBorderOnlyWhileMouseInside {
188 // Always returns YES so that buttons always get mouse tracking even when
189 // disabled. The reload button (and possibly others) depend on this.
190 return YES;
191}
192
193- (void)mouseEntered:(NSEvent*)theEvent {
194 [self setIsMouseInside:YES];
195}
196
197- (void)mouseExited:(NSEvent*)theEvent {
198 [self setIsMouseInside:NO];
199}
200
201@end