katzer/cordova-plugin-badge

View on GitHub
src/osx/DockCircularProgressBar.mm

Summary

Maintainability
Test Coverage
// Borrow from src/chrome/browser/ui/cocoa/dock_icon.mm

// Copyright (c) 2012 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 "DockCircularProgressBar.h"

namespace {

// The fraction of the size of the dock icon that the badge is.
const float kBadgeFraction = 0.4f;
// The indentation of the badge.
const float kBadgeIndent = 5.0f;

class ScopedNSGraphicsContextSaveGState {
 public:
  ScopedNSGraphicsContextSaveGState() {
    [NSGraphicsContext saveGraphicsState];
  }
  ~ScopedNSGraphicsContextSaveGState() {
    [NSGraphicsContext restoreGraphicsState];
  }
};

}

@interface DockTileView : NSView {
@private
  BOOL hide_progress_bar_;
  BOOL show_percent_;
  BOOL indeterminate_;
  //float progress_;
}

// Indicates whether the progress indicator should be in an indeterminate state
// or not.
@property BOOL indeterminate;

// Indicates the amount of progress made of the download. Ranges from [0..1].
@property float progress;

// Indicates whether the progress number should be showed in circular process bar.
@property BOOL showPercent;

@property BOOL hideProgressBar;
@end

@implementation DockTileView

@synthesize indeterminate = indeterminate_;
@synthesize progress = progress_;
@synthesize showPercent = show_percent_;
@synthesize hideProgressBar = hide_progress_bar_;

- (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 (hide_progress_bar_)
    return;
  NSRect badgeRect = [self bounds];
  badgeRect.size.height = (int)(kBadgeFraction * badgeRect.size.height);
  int newWidth = kBadgeFraction * NSWidth(badgeRect);
  badgeRect.origin.x = NSWidth(badgeRect) - 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]];
  NSGradient* backgroundGradient =
      [[NSGradient alloc] initWithStartingColor:backgroundHighlight
                                    endingColor:backgroundColor];
  NSBezierPath* badgeEdge = [NSBezierPath bezierPathWithOvalInRect:badgeRect];
  {
    ScopedNSGraphicsContextSaveGState scopedGState;
    [badgeEdge addClip];
    [backgroundGradient drawFromCenter:badgeCenter
                                radius:0.0
                              toCenter:badgeCenter
                                radius:badgeRadius
                               options:0];
  }
  
  // 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]];
    NSGradient* 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];
    }
    ScopedNSGraphicsContextSaveGState scopedGState;
    [progressSlice addClip];
    [sliceGradient drawFromCenter:badgeCenter
                           radius:0.0
                         toCenter:badgeCenter
                           radius:badgeRadius
                          options:0];
  }
  
  // Edge
  {
    ScopedNSGraphicsContextSaveGState scopedGState;
    [[NSColor whiteColor] set];
    NSShadow* shadow = [[NSShadow alloc] init];
    [shadow setShadowOffset:NSMakeSize(0, -2)];
    [shadow setShadowBlurRadius:2];
    [shadow set];
    [badgeEdge setLineWidth:2];
    [badgeEdge stroke];
  }
  
  if (!show_percent_) return;

  // Show percent. 
  NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
  NSString* countString =
  [formatter stringFromNumber:[NSNumber numberWithInt:(int)(progress_ * 100)]];
  
  NSShadow* countShadow = [[NSShadow alloc] init];
  [countShadow setShadowBlurRadius:3.0];
  [countShadow setShadowColor:[NSColor whiteColor]];
  [countShadow setShadowOffset:NSMakeSize(0.0, 0.0)];
  NSMutableDictionary* countAttrsDict =
  [NSMutableDictionary dictionaryWithObjectsAndKeys:
   [NSColor blackColor], NSForegroundColorAttributeName,
   countShadow, NSShadowAttributeName,
   nil];
  CGFloat countFontSize = badgeRadius;
  NSSize countSize = NSZeroSize;
  NSAttributedString* countAttrString;
  while (1) {
    NSFont* countFont = [NSFont fontWithName:@"Helvetica-Bold"
                                        size:countFontSize];
    
    // This will generally be plain Helvetica.
    if (!countFont)
      countFont = [NSFont userFontOfSize:countFontSize];
    
    // Continued failure would generate an NSException.
    if (!countFont)
      break;
    
    [countAttrsDict setObject:countFont forKey:NSFontAttributeName];
    countAttrString = 
        [[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 drawAtPoint:countOrigin];
}

@end
@implementation DockCircularProgressBar

+ (DockCircularProgressBar*) sharedDockCircularProgressBar {
  static DockCircularProgressBar* progress_bar;
  NSDockTile* dockTile = [NSApp dockTile];
  if (!progress_bar) {
    progress_bar = [[DockCircularProgressBar alloc] init];
  }
  if ([dockTile contentView] == NULL) {
    DockTileView* dockTileView = [[DockTileView alloc] init];
    [dockTile setContentView:dockTileView];
  }
  return progress_bar;
}

- (void)updateProgressBar {
  NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
  DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
  [dockTileView setHideProgressBar:NO];
  [[NSApp dockTile] display];
}

-(void)hideProgressBar {
  NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
  DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
  [dockTileView setHideProgressBar:YES];
  [[NSApp dockTile] display];
}

- (void)setIndeterminate:(BOOL)indeterminate {
  NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
  DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
  
  if (indeterminate != [dockTileView indeterminate]) {
    [dockTileView setIndeterminate:indeterminate];
  }
}

- (void)setProgress:(float) progress {
  NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
  DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
  
  [dockTileView setProgress:progress];
}

- (void)setShowPercent:(BOOL)show_percent {
  NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
  DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
  
  [dockTileView setShowPercent:show_percent];
}

- (void)clear {
  [[NSApp dockTile] setContentView:NULL];
}

- (double) getProgress
{
    NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
    DockTileView* dockTileView = (DockTileView*)([dockTile contentView]);
    
    return (double)dockTileView.progress;
}
@end