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/dock_icon.h"
      6 
      7 #include "base/memory/scoped_nsobject.h"
      8 
      9 // The fraction of the size of the dock icon that the badge is.
     10 static const float kBadgeFraction = 0.4f;
     11 
     12 // The indentation of the badge.
     13 static const float kBadgeIndent = 5.0f;
     14 
     15 // A view that draws our dock tile.
     16 @interface DockTileView : NSView {
     17  @private
     18   int downloads_;
     19   BOOL indeterminate_;
     20   float progress_;
     21 }
     22 
     23 // Indicates how many downloads are in progress.
     24 @property (nonatomic) int downloads;
     25 
     26 // Indicates whether the progress indicator should be in an indeterminate state
     27 // or not.
     28 @property (nonatomic) BOOL indeterminate;
     29 
     30 // Indicates the amount of progress made of the download. Ranges from [0..1].
     31 @property (nonatomic) float progress;
     32 
     33 @end
     34 
     35 @implementation DockTileView
     36 
     37 @synthesize downloads = downloads_;
     38 @synthesize indeterminate = indeterminate_;
     39 @synthesize progress = progress_;
     40 
     41 - (void)drawRect:(NSRect)dirtyRect {
     42   // Not -[NSApplication applicationIconImage]; that fails to return a pasted
     43   // custom icon.
     44   NSString* appPath = [[NSBundle mainBundle] bundlePath];
     45   NSImage* appIcon = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
     46   [appIcon drawInRect:[self bounds]
     47              fromRect:NSZeroRect
     48             operation:NSCompositeSourceOver
     49              fraction:1.0];
     50 
     51   if (downloads_ == 0)
     52     return;
     53 
     54   NSRect badgeRect = [self bounds];
     55   badgeRect.size.height = (int)(kBadgeFraction * badgeRect.size.height);
     56   int newWidth = kBadgeFraction * badgeRect.size.width;
     57   badgeRect.origin.x = badgeRect.size.width - newWidth;
     58   badgeRect.size.width = newWidth;
     59 
     60   CGFloat badgeRadius = NSMidY(badgeRect);
     61 
     62   badgeRect.origin.x -= kBadgeIndent;
     63   badgeRect.origin.y += kBadgeIndent;
     64 
     65   NSPoint badgeCenter = NSMakePoint(NSMidX(badgeRect),
     66                                     NSMidY(badgeRect));
     67 
     68   // Background
     69   NSColor* backgroundColor = [NSColor colorWithCalibratedRed:0.85
     70                                                        green:0.85
     71                                                         blue:0.85
     72                                                        alpha:1.0];
     73   NSColor* backgroundHighlight =
     74       [backgroundColor blendedColorWithFraction:0.85
     75                                         ofColor:[NSColor whiteColor]];
     76   scoped_nsobject<NSGradient> backgroundGradient(
     77       [[NSGradient alloc] initWithStartingColor:backgroundHighlight
     78                                     endingColor:backgroundColor]);
     79   NSBezierPath* badgeEdge = [NSBezierPath bezierPathWithOvalInRect:badgeRect];
     80   [NSGraphicsContext saveGraphicsState];
     81   [badgeEdge addClip];
     82   [backgroundGradient drawFromCenter:badgeCenter
     83                               radius:0.0
     84                             toCenter:badgeCenter
     85                               radius:badgeRadius
     86                              options:0];
     87   [NSGraphicsContext restoreGraphicsState];
     88 
     89   // Slice
     90   if (!indeterminate_) {
     91     NSColor* sliceColor = [NSColor colorWithCalibratedRed:0.45
     92                                                     green:0.8
     93                                                      blue:0.25
     94                                                     alpha:1.0];
     95     NSColor* sliceHighlight =
     96         [sliceColor blendedColorWithFraction:0.4
     97                                      ofColor:[NSColor whiteColor]];
     98     scoped_nsobject<NSGradient> sliceGradient(
     99         [[NSGradient alloc] initWithStartingColor:sliceHighlight
    100                                       endingColor:sliceColor]);
    101     NSBezierPath* progressSlice;
    102     if (progress_ >= 1.0) {
    103       progressSlice = [NSBezierPath bezierPathWithOvalInRect:badgeRect];
    104     } else {
    105       CGFloat endAngle = 90.0 - 360.0 * progress_;
    106       if (endAngle < 0.0)
    107         endAngle += 360.0;
    108       progressSlice = [NSBezierPath bezierPath];
    109       [progressSlice moveToPoint:badgeCenter];
    110       [progressSlice appendBezierPathWithArcWithCenter:badgeCenter
    111                                                 radius:badgeRadius
    112                                             startAngle:90.0
    113                                               endAngle:endAngle
    114                                              clockwise:YES];
    115       [progressSlice closePath];
    116     }
    117     [NSGraphicsContext saveGraphicsState];
    118     [progressSlice addClip];
    119     [sliceGradient drawFromCenter:badgeCenter
    120                            radius:0.0
    121                          toCenter:badgeCenter
    122                            radius:badgeRadius
    123                           options:0];
    124     [NSGraphicsContext restoreGraphicsState];
    125   }
    126 
    127   // Edge
    128   [NSGraphicsContext saveGraphicsState];
    129   [[NSColor whiteColor] set];
    130   scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
    131   [shadow.get() setShadowOffset:NSMakeSize(0, -2)];
    132   [shadow setShadowBlurRadius:2];
    133   [shadow set];
    134   [badgeEdge setLineWidth:2];
    135   [badgeEdge stroke];
    136   [NSGraphicsContext restoreGraphicsState];
    137 
    138   // Download count
    139   scoped_nsobject<NSNumberFormatter> formatter(
    140       [[NSNumberFormatter alloc] init]);
    141   NSString* countString =
    142       [formatter stringFromNumber:[NSNumber numberWithInt:downloads_]];
    143 
    144   scoped_nsobject<NSShadow> countShadow([[NSShadow alloc] init]);
    145   [countShadow setShadowBlurRadius:3.0];
    146   [countShadow.get() setShadowColor:[NSColor whiteColor]];
    147   [countShadow.get() setShadowOffset:NSMakeSize(0.0, 0.0)];
    148   NSMutableDictionary* countAttrsDict =
    149       [NSMutableDictionary dictionaryWithObjectsAndKeys:
    150           [NSColor blackColor], NSForegroundColorAttributeName,
    151           countShadow.get(), NSShadowAttributeName,
    152           nil];
    153   CGFloat countFontSize = badgeRadius;
    154   NSSize countSize = NSZeroSize;
    155   scoped_nsobject<NSAttributedString> countAttrString;
    156   while (1) {
    157     NSFont* countFont = [NSFont fontWithName:@"Helvetica-Bold"
    158                                         size:countFontSize];
    159     [countAttrsDict setObject:countFont forKey:NSFontAttributeName];
    160     countAttrString.reset(
    161         [[NSAttributedString alloc] initWithString:countString
    162                                         attributes:countAttrsDict]);
    163     countSize = [countAttrString size];
    164     if (countSize.width > badgeRadius * 1.5) {
    165       countFontSize -= 1.0;
    166     } else {
    167       break;
    168     }
    169   }
    170 
    171   NSPoint countOrigin = badgeCenter;
    172   countOrigin.x -= countSize.width / 2;
    173   countOrigin.y -= countSize.height / 2.2;  // tweak; otherwise too low
    174 
    175   [countAttrString.get() drawAtPoint:countOrigin];
    176 }
    177 
    178 @end
    179 
    180 
    181 @implementation DockIcon
    182 
    183 + (DockIcon*)sharedDockIcon {
    184   static DockIcon* icon;
    185   if (!icon) {
    186     NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    187 
    188     scoped_nsobject<DockTileView> dockTileView([[DockTileView alloc] init]);
    189     [dockTile setContentView:dockTileView];
    190 
    191     icon = [[DockIcon alloc] init];
    192   }
    193 
    194   return icon;
    195 }
    196 
    197 - (void)updateIcon {
    198   NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    199 
    200   [dockTile display];
    201 }
    202 
    203 - (void)setDownloads:(int)downloads {
    204   NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    205   DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
    206 
    207   [dockTileView setDownloads:downloads];
    208 }
    209 
    210 - (void)setIndeterminate:(BOOL)indeterminate {
    211   NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    212   DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
    213 
    214   [dockTileView setIndeterminate:indeterminate];
    215 }
    216 
    217 - (void)setProgress:(float)progress {
    218   NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    219   DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
    220 
    221   [dockTileView setProgress:progress];
    222 }
    223 
    224 @end
    225