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. 10static const float kBadgeFraction = 0.4f; 11 12// The indentation of the badge. 13static 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