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/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