• 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/rect_path_utils.h"
10#import "chrome/browser/ui/cocoa/themed_window.h"
11#import "ui/base/cocoa/nsview_additions.h"
12#include "ui/gfx/image/image.h"
13#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
14
15// When the window doesn't have focus then we want to draw the button with a
16// slightly lighter color. We do this by just reducing the alpha.
17const CGFloat kImageNoFocusAlpha = 0.65;
18
19@interface ImageButtonCell (Private)
20- (void)sharedInit;
21- (image_button_cell::ButtonState)currentButtonState;
22- (NSImage*)imageForID:(NSInteger)imageID
23           controlView:(NSView*)controlView;
24@end
25
26@implementation ImageButtonCell
27
28@synthesize isMouseInside = isMouseInside_;
29
30// For nib instantiations
31- (id)initWithCoder:(NSCoder*)decoder {
32  if ((self = [super initWithCoder:decoder])) {
33    [self sharedInit];
34  }
35  return self;
36}
37
38// For programmatic instantiations
39- (id)initTextCell:(NSString*)string {
40  if ((self = [super initTextCell:string])) {
41    [self sharedInit];
42  }
43  return self;
44}
45
46- (void)sharedInit {
47  [self setHighlightsBy:NSNoCellMask];
48
49  // We need to set this so that we can override |-mouseEntered:| and
50  // |-mouseExited:| to change the button image on hover states.
51  [self setShowsBorderOnlyWhileMouseInside:YES];
52}
53
54- (NSImage*)imageForState:(image_button_cell::ButtonState)state
55                     view:(NSView*)controlView{
56  if (image_[state].imageId)
57    return [self imageForID:image_[state].imageId controlView:controlView];
58  return image_[state].image;
59}
60
61- (void)drawImageWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
62  image_button_cell::ButtonState state = [self currentButtonState];
63  BOOL windowHasFocus = [[controlView window] isMainWindow] ||
64                        [[controlView window] isKeyWindow];
65  CGFloat alpha = [self imageAlphaForWindowState:[controlView window]];
66  NSImage* image = [self imageForState:state view:controlView];
67
68  if (!windowHasFocus) {
69    NSImage* defaultImage = [self
70      imageForState:image_button_cell::kDefaultStateBackground
71               view:controlView];
72    NSImage* hoverImage = [self
73      imageForState:image_button_cell::kHoverStateBackground
74               view:controlView];
75    if ([self currentButtonState] == image_button_cell::kDefaultState &&
76        defaultImage) {
77      image = defaultImage;
78      alpha = 1.0;
79    } else if ([self currentButtonState] == image_button_cell::kHoverState &&
80        hoverImage) {
81      image = hoverImage;
82      alpha = 1.0;
83    }
84  }
85
86  NSRect imageRect;
87  imageRect.size = [image size];
88  imageRect.origin.x = cellFrame.origin.x +
89    roundf((NSWidth(cellFrame) - NSWidth(imageRect)) / 2.0);
90  imageRect.origin.y = cellFrame.origin.y +
91    roundf((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
92
93  [image drawInRect:imageRect
94           fromRect:NSZeroRect
95          operation:NSCompositeSourceOver
96           fraction:alpha
97     respectFlipped:YES
98              hints:nil];
99}
100
101- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
102  [self drawImageWithFrame:cellFrame inView:controlView];
103  // Only draw custom focus ring if the 10.7 focus ring APIs are not available.
104  // TODO(groby): Remove once we build against the 10.7 SDK.
105  if (![self respondsToSelector:@selector(drawFocusRingMaskWithFrame:inView:)])
106    [self drawFocusRingWithFrame:cellFrame inView:controlView];
107}
108
109- (void)setImageID:(NSInteger)imageID
110    forButtonState:(image_button_cell::ButtonState)state {
111  DCHECK_GE(state, 0);
112  DCHECK_LT(state, image_button_cell::kButtonStateCount);
113
114  image_[state].image.reset();
115  image_[state].imageId = imageID;
116  [[self controlView] setNeedsDisplay:YES];
117}
118
119// Sets the image for the given button state using an image.
120- (void)setImage:(NSImage*)image
121  forButtonState:(image_button_cell::ButtonState)state {
122  DCHECK_GE(state, 0);
123  DCHECK_LT(state, image_button_cell::kButtonStateCount);
124
125  image_[state].image.reset([image retain]);
126  image_[state].imageId = 0;
127  [[self controlView] setNeedsDisplay:YES];
128}
129
130- (CGFloat)imageAlphaForWindowState:(NSWindow*)window {
131  BOOL windowHasFocus = [window isMainWindow] || [window isKeyWindow];
132  return windowHasFocus ? 1.0 : kImageNoFocusAlpha;
133}
134
135- (void)drawFocusRingWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
136  if (![self showsFirstResponder])
137    return;
138  gfx::ScopedNSGraphicsContextSaveGState scoped_state;
139  const CGFloat lineWidth = [controlView cr_lineWidth];
140  rect_path_utils::FrameRectWithInset(rect_path_utils::RoundedCornerAll,
141                                      NSInsetRect(cellFrame, 0, lineWidth),
142                                      0.0,            // insetX
143                                      0.0,            // insetY
144                                      3.0,            // outerRadius
145                                      lineWidth * 2,  // lineWidth
146                                      [controlView
147                                          cr_keyboardFocusIndicatorColor]);
148}
149
150- (image_button_cell::ButtonState)currentButtonState {
151  bool (^has)(image_button_cell::ButtonState) =
152      ^(image_button_cell::ButtonState state) {
153          return image_[state].image || image_[state].imageId;
154      };
155  if (![self isEnabled] && has(image_button_cell::kDisabledState))
156    return image_button_cell::kDisabledState;
157  if ([self isHighlighted] && has(image_button_cell::kPressedState))
158    return image_button_cell::kPressedState;
159  if ([self isMouseInside] && has(image_button_cell::kHoverState))
160    return image_button_cell::kHoverState;
161  return image_button_cell::kDefaultState;
162}
163
164- (NSImage*)imageForID:(NSInteger)imageID
165           controlView:(NSView*)controlView {
166  if (!imageID)
167    return nil;
168
169  ui::ThemeProvider* themeProvider = [[controlView window] themeProvider];
170  if (!themeProvider)
171    return nil;
172
173  return themeProvider->GetNSImageNamed(imageID);
174}
175
176- (void)setIsMouseInside:(BOOL)isMouseInside {
177  if (isMouseInside_ != isMouseInside) {
178    isMouseInside_ = isMouseInside;
179    NSView<ImageButton>* control =
180        static_cast<NSView<ImageButton>*>([self controlView]);
181    if ([control respondsToSelector:@selector(mouseInsideStateDidChange:)]) {
182      [control mouseInsideStateDidChange:isMouseInside];
183    }
184    [control setNeedsDisplay:YES];
185  }
186}
187
188- (void)setShowsBorderOnlyWhileMouseInside:(BOOL)show {
189  VLOG_IF(1, !show) << "setShowsBorderOnlyWhileMouseInside:NO ignored";
190}
191
192- (BOOL)showsBorderOnlyWhileMouseInside {
193  // Always returns YES so that buttons always get mouse tracking even when
194  // disabled. The reload button (and possibly others) depend on this.
195  return YES;
196}
197
198- (void)mouseEntered:(NSEvent*)theEvent {
199  [self setIsMouseInside:YES];
200}
201
202- (void)mouseExited:(NSEvent*)theEvent {
203  [self setIsMouseInside:NO];
204}
205
206@end
207