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. 18 const 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)drawWithFrame:(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 [self drawFocusRingWithFrame:cellFrame inView:controlView]; 102 } 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); 108 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]; 123 } 124 125 - (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]); 143 } 144 145 - (image_button_cell::ButtonState)currentButtonState { 146 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)) 151 return image_button_cell::kDisabledState; 152 if ([self isHighlighted] && has(image_button_cell::kPressedState)) 153 return image_button_cell::kPressedState; 154 if ([self isMouseInside] && has(image_button_cell::kHoverState)) 155 return image_button_cell::kHoverState; 156 return image_button_cell::kDefaultState; 157 } 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 168 return themeProvider->GetNSImageNamed(imageID); 169 } 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 202