/* HBToolbarBadgedItem.m This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HBToolbarBadgedItem.h" @interface HBToolbarBadgedItem () @property (nonatomic) NSImage *primary; @property (nonatomic) NSImage *cache; @end @implementation HBToolbarBadgedItem - (instancetype)initWithItemIdentifier:(NSString *)itemIdentifier { self = [super initWithItemIdentifier:itemIdentifier]; if (self) { _badgeFillColor = [NSColor redColor]; _badgeTextColor = [NSColor whiteColor]; } return self; } - (void)awakeFromNib { if ([self respondsToSelector:@selector(awakeFromNib)]) { [super awakeFromNib]; } [self HB_refreshBadge]; } #pragma mark - Public - (void)setImage:(NSImage *)image { _primary = image; if (_badgeValue.length) { _cache = nil; [super setImage:[self HB_badgeImage:_badgeValue]]; } else { [super setImage:image]; } } - (void)setBadgeValue:(NSString *)badgeValue { if (![_badgeValue isEqualToString:badgeValue]) { if (badgeValue.length) { [super setImage:[self HB_badgeImage:badgeValue]]; } else { [super setImage:_primary]; } _badgeValue = [badgeValue copy]; } } - (void)setBadgeTextColor:(NSColor *)badgeTextColor { _badgeTextColor = [badgeTextColor copy]; [self HB_refreshBadge]; } - (void)setBadgeFillColor:(NSColor *)badgeFillColor { _badgeFillColor = [badgeFillColor copy]; [self HB_refreshBadge]; } #pragma mark -- Private Methods - (CGColorRef)copyNSColorToCGColor:(NSColor *)color { // CGColor property of NSColor has been added only in 10.8, // we need to support 10.7 too. NSInteger numberOfComponents = [color numberOfComponents]; CGFloat components[numberOfComponents]; CGColorSpaceRef colorSpace = [[color colorSpace] CGColorSpace]; [color getComponents:(CGFloat *)&components]; CGColorRef cgColor = CGColorCreate(colorSpace, components); return cgColor; } - (void)HB_refreshBadge { if (_badgeValue.length) { _cache = [self HB_renderImage:_primary withBadge:_badgeValue]; [super setImage:_cache]; } } - (NSImage *)HB_badgeImage:(NSString *)badgeValue { if (![_badgeValue isEqualToString:badgeValue] || _cache == nil) { _cache = [self HB_renderImage:_primary withBadge:badgeValue]; } return _cache; } - (NSImage *)HB_renderImage:(NSImage *)image withBadge:(NSString *)badgeString { NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSImage *newImage = [[NSImage alloc] initWithSize:image.size]; for (NSImageRep *rep in image.representations) { NSSize size = NSMakeSize(rep.pixelsWide, rep.pixelsHigh); NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:(NSInteger)floor(size.width) pixelsHigh:(NSInteger)floor(size.height) bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:(NSInteger)floor(size.width) * 4 bitsPerPixel:32]; NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:newRep]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:ctx]; CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextSaveGState(context); NSRect imageRect = NSMakeRect(0, 0, size.width, size.height); CGImageRef ref = [image CGImageForProposedRect:&imageRect context:[NSGraphicsContext currentContext] hints:nil]; CGContextDrawImage(context, imageRect, ref); // Work out the area CGFloat scaleFactor = rep.pixelsWide / rep.size.width; CGFloat typeSize = 10; if (scaleFactor > 1) { typeSize = 8; } CGFloat pointSize = typeSize * scaleFactor; NSFont *font = [NSFont boldSystemFontOfSize:pointSize]; NSDictionary *attr = @{NSParagraphStyleAttributeName : paragraphStyle, NSFontAttributeName : font, NSForegroundColorAttributeName : _badgeTextColor }; NSRect textBounds = [badgeString boundingRectWithSize:NSZeroSize options:0 attributes:attr]; NSPoint indent = NSMakePoint(typeSize * scaleFactor, 2 * scaleFactor); CGFloat radius = (textBounds.size.height + indent.y) * 0.5f; CGFloat offset_x = 0; CGFloat offset_y = 0; if (scaleFactor > 1) { offset_y = 2 * scaleFactor; } NSRect badgeRect = NSMakeRect(size.width - textBounds.size.width - indent.x - offset_x, offset_y, textBounds.size.width + indent.x, textBounds.size.height + indent.y); badgeRect = NSIntegralRect(badgeRect); // Draw the ellipse CGFloat minx = CGRectGetMinX(badgeRect); CGFloat midx = CGRectGetMidX(badgeRect); CGFloat maxx = CGRectGetMaxX(badgeRect); CGFloat miny = CGRectGetMinY(badgeRect); CGFloat midy = CGRectGetMidY(badgeRect); CGFloat maxy = CGRectGetMaxY(badgeRect); // Fill the ellipse CGContextSaveGState(context); CGContextBeginPath(context); CGContextMoveToPoint(context, minx, midy); CGContextAddArcToPoint(context, minx, miny, midx, miny, radius); CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius); CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius); CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius); CGContextClosePath(context); CGColorRef fillColor = [self copyNSColorToCGColor:_badgeFillColor]; CGContextSetFillColorWithColor(context,fillColor); CFRelease(fillColor); CGContextDrawPath(context, kCGPathFill); // Draw the text badgeRect.origin.x = CGRectGetMidX(badgeRect); badgeRect.origin.x -= textBounds.origin.x / 2; badgeRect.origin.x -= ((textBounds.size.width - textBounds.origin.x) * 0.5f); badgeRect.origin.y = CGRectGetMidY(badgeRect); badgeRect.origin.y -= textBounds.origin.y / 2; badgeRect.origin.y -= ((textBounds.size.height - textBounds.origin.y) * 0.5f); badgeRect.origin.x = floor(badgeRect.origin.x); badgeRect.origin.y = floor(badgeRect.origin.y); badgeRect.size.width = textBounds.size.width; badgeRect.size.height = textBounds.size.height; [badgeString drawInRect:badgeRect withAttributes:attr]; CGContextRestoreGState(context); CGContextFlush(context); CGContextRestoreGState(context); [NSGraphicsContext restoreGraphicsState]; [newImage addRepresentation:newRep]; } return newImage; } @end