• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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"
9#import "chrome/browser/ui/cocoa/nsview_additions.h"
10#import "chrome/browser/ui/cocoa/rect_path_utils.h"
11#import "chrome/browser/ui/cocoa/themed_window.h"
12#include "ui/base/resource/resource_bundle.h"
13#include "ui/gfx/image/image.h"
14#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
15
16// 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
20@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
29@synthesize isMouseInside = isMouseInside_;
30
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
55- (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
62- (void)drawImageWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
63  image_button_cell::ButtonState state = [self currentButtonState];
64  BOOL windowHasFocus = [[controlView window] isMainWindow] ||
65                        [[controlView window] isKeyWindow];
66  CGFloat alpha = [self imageAlphaForWindowState:[controlView window]];
67  NSImage* image = [self imageForState:state view:controlView];
68
69  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
87  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}
101
102- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
103  [self drawImageWithFrame:cellFrame inView:controlView];
104  // Only draw custom focus ring if the 10.7 focus ring APIs are not available.
105  // TODO(groby): Remove once we build against the 10.7 SDK.
106  if (![self respondsToSelector:@selector(drawFocusRingMaskWithFrame:inView:)])
107    [self drawFocusRingWithFrame:cellFrame inView:controlView];
108}
109
110- (void)setImageID:(NSInteger)imageID
111    forButtonState:(image_button_cell::ButtonState)state {
112  DCHECK_GE(state, 0);
113  DCHECK_LT(state, image_button_cell::kButtonStateCount);
114
115  image_[state].image.reset();
116  image_[state].imageId = imageID;
117  [[self controlView] setNeedsDisplay:YES];
118}
119
120// Sets the image for the given button state using an image.
121- (void)setImage:(NSImage*)image
122  forButtonState:(image_button_cell::ButtonState)state {
123  DCHECK_GE(state, 0);
124  DCHECK_LT(state, image_button_cell::kButtonStateCount);
125
126  image_[state].image.reset([image retain]);
127  image_[state].imageId = 0;
128  [[self controlView] setNeedsDisplay:YES];
129}
130
131- (CGFloat)imageAlphaForWindowState:(NSWindow*)window {
132  BOOL windowHasFocus = [window isMainWindow] || [window isKeyWindow];
133  return windowHasFocus ? 1.0 : kImageNoFocusAlpha;
134}
135
136- (void)drawFocusRingWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
137  if (![self showsFirstResponder])
138    return;
139  gfx::ScopedNSGraphicsContextSaveGState scoped_state;
140  const CGFloat lineWidth = [controlView cr_lineWidth];
141  rect_path_utils::FrameRectWithInset(rect_path_utils::RoundedCornerAll,
142                                      NSInsetRect(cellFrame, 0, lineWidth),
143                                      0.0,            // insetX
144                                      0.0,            // insetY
145                                      3.0,            // outerRadius
146                                      lineWidth * 2,  // lineWidth
147                                      [controlView
148                                          cr_keyboardFocusIndicatorColor]);
149}
150
151- (image_button_cell::ButtonState)currentButtonState {
152  bool (^has)(image_button_cell::ButtonState) =
153      ^(image_button_cell::ButtonState state) {
154          return image_[state].image || image_[state].imageId;
155      };
156  if (![self isEnabled] && has(image_button_cell::kDisabledState))
157    return image_button_cell::kDisabledState;
158  if ([self isHighlighted] && has(image_button_cell::kPressedState))
159    return image_button_cell::kPressedState;
160  if ([self isMouseInside] && has(image_button_cell::kHoverState))
161    return image_button_cell::kHoverState;
162  return image_button_cell::kDefaultState;
163}
164
165- (NSImage*)imageForID:(NSInteger)imageID
166           controlView:(NSView*)controlView {
167  if (!imageID)
168    return nil;
169
170  ui::ThemeProvider* themeProvider = [[controlView window] themeProvider];
171  if (!themeProvider)
172    return nil;
173
174  return themeProvider->GetNSImageNamed(imageID);
175}
176
177- (void)setIsMouseInside:(BOOL)isMouseInside {
178  if (isMouseInside_ != isMouseInside) {
179    isMouseInside_ = isMouseInside;
180    NSView<ImageButton>* control =
181        static_cast<NSView<ImageButton>*>([self controlView]);
182    if ([control respondsToSelector:@selector(mouseInsideStateDidChange:)]) {
183      [control mouseInsideStateDidChange:isMouseInside];
184    }
185    [control setNeedsDisplay:YES];
186  }
187}
188
189- (void)setShowsBorderOnlyWhileMouseInside:(BOOL)show {
190  VLOG_IF(1, !show) << "setShowsBorderOnlyWhileMouseInside:NO ignored";
191}
192
193- (BOOL)showsBorderOnlyWhileMouseInside {
194  // Always returns YES so that buttons always get mouse tracking even when
195  // disabled. The reload button (and possibly others) depend on this.
196  return YES;
197}
198
199- (void)mouseEntered:(NSEvent*)theEvent {
200  [self setIsMouseInside:YES];
201}
202
203- (void)mouseExited:(NSEvent*)theEvent {
204  [self setIsMouseInside:NO];
205}
206
207@end
208