Home | History | Annotate | Download | only in controls
      1 // Copyright 2013 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 "ui/base/cocoa/controls/hyperlink_button_cell.h"
      6 
      7 @interface HyperlinkButtonCell ()
      8 - (void)customizeButtonCell;
      9 @end
     10 
     11 @implementation HyperlinkButtonCell
     12 
     13 @dynamic textColor;
     14 @synthesize underlineOnHover = underlineOnHover_;
     15 @synthesize shouldUnderline = shouldUnderline_;
     16 
     17 + (NSColor*)defaultTextColor {
     18   return [NSColor blueColor];
     19 }
     20 
     21 + (NSButton*)buttonWithString:(NSString*)string {
     22   NSButton* button = [[[NSButton alloc] initWithFrame:NSZeroRect] autorelease];
     23   base::scoped_nsobject<HyperlinkButtonCell> cell(
     24       [[HyperlinkButtonCell alloc] initTextCell:string]);
     25   [cell setAlignment:NSLeftTextAlignment];
     26   [button setCell:cell.get()];
     27   [button setBezelStyle:NSRegularSquareBezelStyle];
     28   return button;
     29 }
     30 
     31 // Designated initializer.
     32 - (id)init {
     33   if ((self = [super init])) {
     34     [self customizeButtonCell];
     35   }
     36   return self;
     37 }
     38 
     39 // Initializer called when the cell is loaded from the NIB.
     40 - (id)initWithCoder:(NSCoder*)aDecoder {
     41   if ((self = [super initWithCoder:aDecoder])) {
     42     [self customizeButtonCell];
     43   }
     44   return self;
     45 }
     46 
     47 // Initializer for code-based creation.
     48 - (id)initTextCell:(NSString*)title {
     49   if ((self = [super initTextCell:title])) {
     50     [self customizeButtonCell];
     51   }
     52   return self;
     53 }
     54 
     55 - (id)copyWithZone:(NSZone*)zone {
     56   NSColor* color = textColor_.release();
     57   HyperlinkButtonCell* cell = [super copyWithZone:zone];
     58   cell->textColor_.reset([color copy]);
     59   textColor_.reset(color);
     60   return cell;
     61 }
     62 
     63 // Because an NSButtonCell has multiple initializers, this method performs the
     64 // common cell customization code.
     65 - (void)customizeButtonCell {
     66   [self setBordered:NO];
     67   [self setTextColor:[HyperlinkButtonCell defaultTextColor]];
     68   [self setShouldUnderline:YES];
     69 
     70   CGFloat fontSize = [NSFont systemFontSizeForControlSize:[self controlSize]];
     71   NSFont* font = [NSFont controlContentFontOfSize:fontSize];
     72   [self setFont:font];
     73 
     74   // Do not change button appearance when we are pushed.
     75   [self setHighlightsBy:NSNoCellMask];
     76 
     77   // We need to set this so that we can override |-mouseEntered:| and
     78   // |-mouseExited:| to change the cursor style on hover states.
     79   [self setShowsBorderOnlyWhileMouseInside:YES];
     80 }
     81 
     82 - (void)setControlSize:(NSControlSize)size {
     83   [super setControlSize:size];
     84   [self customizeButtonCell];  // recompute |font|.
     85 }
     86 
     87 // Creates the NSDictionary of attributes for the attributed string.
     88 - (NSDictionary*)linkAttributes {
     89   NSUInteger underlineMask = NSNoUnderlineStyle;
     90   if (shouldUnderline_ &&
     91       (!underlineOnHover_ || (mouseIsInside_ && [self isEnabled])))
     92     underlineMask = NSUnderlinePatternSolid | NSUnderlineStyleSingle;
     93 
     94   base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
     95       [[NSParagraphStyle defaultParagraphStyle] mutableCopy]);
     96   [paragraphStyle setAlignment:[self alignment]];
     97   [paragraphStyle setLineBreakMode:[self lineBreakMode]];
     98 
     99   return [NSDictionary dictionaryWithObjectsAndKeys:
    100       [self textColor], NSForegroundColorAttributeName,
    101       [NSNumber numberWithInt:underlineMask], NSUnderlineStyleAttributeName,
    102       [self font], NSFontAttributeName,
    103       [NSCursor pointingHandCursor], NSCursorAttributeName,
    104       paragraphStyle.get(), NSParagraphStyleAttributeName,
    105       nil
    106   ];
    107 }
    108 
    109 // Override the drawing for the cell so that the custom style attributes
    110 // can always be applied and so that ellipses will appear when appropriate.
    111 - (NSRect)drawTitle:(NSAttributedString*)title
    112           withFrame:(NSRect)frame
    113              inView:(NSView*)controlView {
    114   NSDictionary* linkAttributes = [self linkAttributes];
    115   NSString* plainTitle = [title string];
    116   [plainTitle drawWithRect:frame
    117                    options:(NSStringDrawingUsesLineFragmentOrigin |
    118                             NSStringDrawingTruncatesLastVisibleLine)
    119                 attributes:linkAttributes];
    120   return frame;
    121 }
    122 
    123 // Override the default behavior to draw the border. Instead, change the cursor.
    124 - (void)mouseEntered:(NSEvent*)event {
    125   mouseIsInside_ = YES;
    126   if ([self isEnabled])
    127     [[NSCursor pointingHandCursor] push];
    128   else
    129     [[NSCursor currentCursor] push];
    130   if (underlineOnHover_)
    131     [[self controlView] setNeedsDisplay:YES];
    132 }
    133 
    134 - (void)mouseExited:(NSEvent*)event {
    135   mouseIsInside_ = NO;
    136   [NSCursor pop];
    137   if (underlineOnHover_)
    138     [[self controlView] setNeedsDisplay:YES];
    139 }
    140 
    141 // Setters and getters.
    142 - (NSColor*)textColor {
    143   if ([self isEnabled])
    144     return textColor_.get();
    145   else
    146     return [NSColor disabledControlTextColor];
    147 }
    148 
    149 - (void)setTextColor:(NSColor*)color {
    150   textColor_.reset([color retain]);
    151 }
    152 
    153 // Override so that |-sizeToFit| works better with this type of cell.
    154 - (NSSize)cellSize {
    155   NSSize size = [super cellSize];
    156   size.width += 2;
    157   return size;
    158 }
    159 
    160 @end
    161