// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "chrome/browser/ui/cocoa/dock_icon.h" #include "base/memory/scoped_nsobject.h" // The fraction of the size of the dock icon that the badge is. static const float kBadgeFraction = 0.4f; // The indentation of the badge. static const float kBadgeIndent = 5.0f; // A view that draws our dock tile. @interface DockTileView : NSView { @private int downloads_; BOOL indeterminate_; float progress_; } // Indicates how many downloads are in progress. @property (nonatomic) int downloads; // Indicates whether the progress indicator should be in an indeterminate state // or not. @property (nonatomic) BOOL indeterminate; // Indicates the amount of progress made of the download. Ranges from [0..1]. @property (nonatomic) float progress; @end @implementation DockTileView @synthesize downloads = downloads_; @synthesize indeterminate = indeterminate_; @synthesize progress = progress_; - (void)drawRect:(NSRect)dirtyRect { // Not -[NSApplication applicationIconImage]; that fails to return a pasted // custom icon. NSString* appPath = [[NSBundle mainBundle] bundlePath]; NSImage* appIcon = [[NSWorkspace sharedWorkspace] iconForFile:appPath]; [appIcon drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; if (downloads_ == 0) return; NSRect badgeRect = [self bounds]; badgeRect.size.height = (int)(kBadgeFraction * badgeRect.size.height); int newWidth = kBadgeFraction * badgeRect.size.width; badgeRect.origin.x = badgeRect.size.width - newWidth; badgeRect.size.width = newWidth; CGFloat badgeRadius = NSMidY(badgeRect); badgeRect.origin.x -= kBadgeIndent; badgeRect.origin.y += kBadgeIndent; NSPoint badgeCenter = NSMakePoint(NSMidX(badgeRect), NSMidY(badgeRect)); // Background NSColor* backgroundColor = [NSColor colorWithCalibratedRed:0.85 green:0.85 blue:0.85 alpha:1.0]; NSColor* backgroundHighlight = [backgroundColor blendedColorWithFraction:0.85 ofColor:[NSColor whiteColor]]; scoped_nsobject backgroundGradient( [[NSGradient alloc] initWithStartingColor:backgroundHighlight endingColor:backgroundColor]); NSBezierPath* badgeEdge = [NSBezierPath bezierPathWithOvalInRect:badgeRect]; [NSGraphicsContext saveGraphicsState]; [badgeEdge addClip]; [backgroundGradient drawFromCenter:badgeCenter radius:0.0 toCenter:badgeCenter radius:badgeRadius options:0]; [NSGraphicsContext restoreGraphicsState]; // Slice if (!indeterminate_) { NSColor* sliceColor = [NSColor colorWithCalibratedRed:0.45 green:0.8 blue:0.25 alpha:1.0]; NSColor* sliceHighlight = [sliceColor blendedColorWithFraction:0.4 ofColor:[NSColor whiteColor]]; scoped_nsobject sliceGradient( [[NSGradient alloc] initWithStartingColor:sliceHighlight endingColor:sliceColor]); NSBezierPath* progressSlice; if (progress_ >= 1.0) { progressSlice = [NSBezierPath bezierPathWithOvalInRect:badgeRect]; } else { CGFloat endAngle = 90.0 - 360.0 * progress_; if (endAngle < 0.0) endAngle += 360.0; progressSlice = [NSBezierPath bezierPath]; [progressSlice moveToPoint:badgeCenter]; [progressSlice appendBezierPathWithArcWithCenter:badgeCenter radius:badgeRadius startAngle:90.0 endAngle:endAngle clockwise:YES]; [progressSlice closePath]; } [NSGraphicsContext saveGraphicsState]; [progressSlice addClip]; [sliceGradient drawFromCenter:badgeCenter radius:0.0 toCenter:badgeCenter radius:badgeRadius options:0]; [NSGraphicsContext restoreGraphicsState]; } // Edge [NSGraphicsContext saveGraphicsState]; [[NSColor whiteColor] set]; scoped_nsobject shadow([[NSShadow alloc] init]); [shadow.get() setShadowOffset:NSMakeSize(0, -2)]; [shadow setShadowBlurRadius:2]; [shadow set]; [badgeEdge setLineWidth:2]; [badgeEdge stroke]; [NSGraphicsContext restoreGraphicsState]; // Download count scoped_nsobject formatter( [[NSNumberFormatter alloc] init]); NSString* countString = [formatter stringFromNumber:[NSNumber numberWithInt:downloads_]]; scoped_nsobject countShadow([[NSShadow alloc] init]); [countShadow setShadowBlurRadius:3.0]; [countShadow.get() setShadowColor:[NSColor whiteColor]]; [countShadow.get() setShadowOffset:NSMakeSize(0.0, 0.0)]; NSMutableDictionary* countAttrsDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSColor blackColor], NSForegroundColorAttributeName, countShadow.get(), NSShadowAttributeName, nil]; CGFloat countFontSize = badgeRadius; NSSize countSize = NSZeroSize; scoped_nsobject countAttrString; while (1) { NSFont* countFont = [NSFont fontWithName:@"Helvetica-Bold" size:countFontSize]; [countAttrsDict setObject:countFont forKey:NSFontAttributeName]; countAttrString.reset( [[NSAttributedString alloc] initWithString:countString attributes:countAttrsDict]); countSize = [countAttrString size]; if (countSize.width > badgeRadius * 1.5) { countFontSize -= 1.0; } else { break; } } NSPoint countOrigin = badgeCenter; countOrigin.x -= countSize.width / 2; countOrigin.y -= countSize.height / 2.2; // tweak; otherwise too low [countAttrString.get() drawAtPoint:countOrigin]; } @end @implementation DockIcon + (DockIcon*)sharedDockIcon { static DockIcon* icon; if (!icon) { NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile]; scoped_nsobject dockTileView([[DockTileView alloc] init]); [dockTile setContentView:dockTileView]; icon = [[DockIcon alloc] init]; } return icon; } - (void)updateIcon { NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile]; [dockTile display]; } - (void)setDownloads:(int)downloads { NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile]; DockTileView* dockTileView = (DockTileView*)([dockTile contentView]); [dockTileView setDownloads:downloads]; } - (void)setIndeterminate:(BOOL)indeterminate { NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile]; DockTileView* dockTileView = (DockTileView*)([dockTile contentView]); [dockTileView setIndeterminate:indeterminate]; } - (void)setProgress:(float)progress { NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile]; DockTileView* dockTileView = (DockTileView*)([dockTile contentView]); [dockTileView setProgress:progress]; } @end