Home | History | Annotate | Download | only in cocoa
      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.
     17 const 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