Home | History | Annotate | Download | only in cocoa
      1 // Copyright (c) 2013 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 "ui/message_center/cocoa/status_item_view.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/format_macros.h"
     10 #include "base/mac/sdk_forward_declarations.h"
     11 #include "ui/base/resource/resource_bundle.h"
     12 #include "ui/resources/grit/ui_resources.h"
     13 
     14 // The width of the status bar item when it's just the icon.
     15 const CGFloat kStatusItemLength = 26;
     16 
     17 // The amount of space between the left and right edges and the content of the
     18 // status item.
     19 const CGFloat kMargin = 5;
     20 
     21 
     22 @interface MCStatusItemView (Private)
     23 // Whether or not the status item should be drawn highlighted.
     24 - (BOOL)shouldHighlight;
     25 
     26 - (int)getTrayResourceId;
     27 @end
     28 
     29 @implementation MCStatusItemView
     30 
     31 @synthesize highlight = highlight_;
     32 
     33 - (id)init {
     34   statusItem_.reset([[[NSStatusBar systemStatusBar] statusItemWithLength:
     35       NSVariableStatusItemLength] retain]);
     36   CGFloat thickness = [[statusItem_ statusBar] thickness];
     37 
     38   NSRect frame = NSMakeRect(0, 0, kStatusItemLength, thickness);
     39   if ((self = [super initWithFrame:frame])) {
     40     [statusItem_ setView:self];
     41   }
     42   return self;
     43 }
     44 
     45 - (void)removeItem {
     46   [[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
     47   statusItem_.reset();
     48 }
     49 
     50 - (size_t)unreadCount {
     51   return unreadCount_;
     52 }
     53 
     54 - (message_center::StatusItemClickedCallback)callback {
     55   return callback_.get();
     56 }
     57 
     58 - (void)setCallback:(message_center::StatusItemClickedCallback)callback {
     59   callback_.reset(callback, base::scoped_policy::RETAIN);
     60 }
     61 
     62 - (void)setUnreadCount:(size_t)unreadCount withQuietMode:(BOOL)quietMode {
     63   unreadCount_ = unreadCount;
     64   quietMode_ = quietMode;
     65 
     66   NSRect frame = [self frame];
     67   frame.size.width = kStatusItemLength;
     68   [self setFrame:frame];
     69 
     70   [self setNeedsDisplay:YES];
     71 }
     72 
     73 - (void)setHighlight:(BOOL)highlight {
     74   highlight_ = highlight;
     75   [self setNeedsDisplay:YES];
     76 }
     77 
     78 - (void)mouseDown:(NSEvent*)event {
     79   inMouseEventSequence_ = YES;
     80   [self setNeedsDisplay:YES];
     81 
     82   if (callback_)
     83     callback_.get()();
     84 }
     85 
     86 - (void)mouseUp:(NSEvent*)event {
     87   inMouseEventSequence_ = NO;
     88   [self setNeedsDisplay:YES];
     89 }
     90 
     91 - (void)rightMouseDown:(NSEvent*)event {
     92   [self mouseDown:event];
     93 }
     94 
     95 - (void)rightMouseUp:(NSEvent*)event {
     96   [self mouseUp:event];
     97 }
     98 
     99 - (void)otherMouseDown:(NSEvent*)event {
    100   [self mouseDown:event];
    101 }
    102 
    103 - (void)otherMouseUp:(NSEvent*)event {
    104   [self mouseUp:event];
    105 }
    106 
    107 - (void)drawRect:(NSRect)dirtyRect {
    108   NSRect frame = [self bounds];
    109 
    110   // Draw the background color.
    111   BOOL highlight = [self shouldHighlight];
    112   [statusItem_ drawStatusBarBackgroundInRect:frame
    113                                withHighlight:highlight];
    114 
    115   int resource_id = [self getTrayResourceId];
    116   // Draw the icon.
    117   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    118   NSImage* image = rb.GetNativeImageNamed(resource_id).ToNSImage();
    119   NSSize size = [image size];
    120   NSRect drawRect = NSMakeRect(kMargin,
    121                                floorf((NSHeight(frame) - size.height) / 2),
    122                                size.width,
    123                                size.height);
    124   [image drawInRect:drawRect
    125            fromRect:NSZeroRect
    126           operation:NSCompositeSourceOver
    127            fraction:1.0];
    128 }
    129 
    130 - (NSArray*)accessibilityActionNames {
    131   return @[ NSAccessibilityPressAction ];
    132 }
    133 
    134 - (void)accessibilityPerformAction:(NSString*)action {
    135   if ([action isEqualToString:NSAccessibilityPressAction]) {
    136     if (callback_)
    137       callback_.get()();
    138     return;
    139   }
    140   [super accessibilityPerformAction:action];
    141 }
    142 
    143 // Private /////////////////////////////////////////////////////////////////////
    144 
    145 - (BOOL)shouldHighlight {
    146   return highlight_ || inMouseEventSequence_;
    147 }
    148 
    149 - (int)getTrayResourceId {
    150   BOOL highlight = [self shouldHighlight];
    151   BOOL hasUnreadItems = unreadCount_ > 0;
    152   BOOL dark = NO;
    153 
    154   Class nsAppearanceClass = NSClassFromString(@"NSAppearance");
    155   if ([self respondsToSelector:@selector(effectiveAppearance)] &&
    156       [nsAppearanceClass respondsToSelector:@selector(appearanceNamed:)]) {
    157     id<NSObject> darkAppearance =
    158         [nsAppearanceClass appearanceNamed:NSAppearanceNameVibrantDark];
    159     dark = [[self effectiveAppearance] isEqual:darkAppearance];
    160   }
    161 
    162   int kResourceIds[2][2][2][2] = {
    163     {
    164       {
    165         { IDR_TRAY_EMPTY, IDR_TRAY_EMPTY_PRESSED },
    166         { IDR_TRAY_ATTENTION, IDR_TRAY_ATTENTION_PRESSED },
    167       },
    168       {
    169         { IDR_TRAY_DO_NOT_DISTURB_EMPTY,
    170           IDR_TRAY_DO_NOT_DISTURB_EMPTY_PRESSED },
    171         { IDR_TRAY_DO_NOT_DISTURB_ATTENTION,
    172           IDR_TRAY_DO_NOT_DISTURB_ATTENTION_PRESSED },
    173       },
    174     },
    175     {
    176       {
    177         // We chose not to support the empty version of the pressed
    178         // resource for the dark theme, so we use the same resource
    179         // for both "pressed" options.
    180         { IDR_DARK_TRAY_EMPTY, IDR_DARK_TRAY_PRESSED },
    181         { IDR_DARK_TRAY_ATTENTION, IDR_DARK_TRAY_PRESSED },
    182       },
    183       {
    184         { IDR_DARK_TRAY_DO_NOT_DISTURB_EMPTY,
    185           IDR_DARK_TRAY_DO_NOT_DISTURB_PRESSED },
    186         { IDR_DARK_TRAY_DO_NOT_DISTURB_ATTENTION,
    187           IDR_DARK_TRAY_DO_NOT_DISTURB_PRESSED },
    188       },
    189     }
    190   };
    191   return kResourceIds[dark][quietMode_][hasUnreadItems][highlight];
    192 }
    193 
    194 @end
    195