diff --git a/atom/browser/ui/tray_icon_cocoa.h b/atom/browser/ui/tray_icon_cocoa.h index 5723cb6b219..d4cfd431e46 100644 --- a/atom/browser/ui/tray_icon_cocoa.h +++ b/atom/browser/ui/tray_icon_cocoa.h @@ -13,7 +13,7 @@ #include "base/mac/scoped_nsobject.h" @class AtomMenuController; -@class StatusItemController; +@class StatusItemView; namespace atom { @@ -30,9 +30,8 @@ class TrayIconCocoa : public TrayIcon { void SetContextMenu(ui::SimpleMenuModel* menu_model) override; private: - base::scoped_nsobject item_; - - base::scoped_nsobject controller_; + // Atom custom view for NSStatusItem. + base::scoped_nsobject status_item_view_; // Status menu shown when right-clicking the system icon. base::scoped_nsobject menu_; diff --git a/atom/browser/ui/tray_icon_cocoa.mm b/atom/browser/ui/tray_icon_cocoa.mm index f989b9b580e..11efbe0aee9 100644 --- a/atom/browser/ui/tray_icon_cocoa.mm +++ b/atom/browser/ui/tray_icon_cocoa.mm @@ -9,35 +9,142 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/screen.h" -@interface StatusItemController : NSObject { + +const CGFloat kStatusItemLength = 26; +const CGFloat kMargin = 3; + +@interface StatusItemView : NSView { atom::TrayIconCocoa* trayIcon_; // weak + AtomMenuController* menu_controller_; // weak + BOOL isHighlightEnable_; + BOOL inMouseEventSequence_; + base::scoped_nsobject image_; + base::scoped_nsobject alternateImage_; + base::scoped_nsobject title_; + base::scoped_nsobject statusItem_; } -- (id)initWithIcon:(atom::TrayIconCocoa*)icon; -- (void)handleClick:(id)sender; -- (void)handleDoubleClick:(id)sender; -@end // @interface StatusItemController +@end // @interface StatusItemView -@implementation StatusItemController +@implementation StatusItemView - (id)initWithIcon:(atom::TrayIconCocoa*)icon { trayIcon_ = icon; + isHighlightEnable_ = YES; + statusItem_.reset([[[NSStatusBar systemStatusBar] statusItemWithLength: + NSVariableStatusItemLength] retain]); + NSRect frame = NSMakeRect(0, 0, 26, [[statusItem_ statusBar] thickness]); + if ((self = [super initWithFrame:frame])) { + [statusItem_ setView:self]; + } return self; } -- (void)handleClick:(id)sender { - // Get the frame of the NSStatusItem. - NSRect frame = [NSApp currentEvent].window.frame; - gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); - // Flip coordinates to gfx (0,0 in top-left corner) using current screen. - NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; - bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); - - trayIcon_->NotifyClicked(bounds); +- (void)removeItem { + [[NSStatusBar systemStatusBar] removeStatusItem:statusItem_]; + statusItem_.reset(); } -- (void)handleDoubleClick:(id)sender { - trayIcon_->NotifyDoubleClicked(); +- (void)drawRect:(NSRect)dirtyRect { + BOOL highlight = [self shouldHighlight]; + [statusItem_ drawStatusBarBackgroundInRect:[self bounds] + withHighlight:highlight]; + NSRect icon_frame = NSMakeRect(0, + 0, + kStatusItemLength, + [[statusItem_ statusBar] thickness]); + NSRect icon_draw_rect = NSInsetRect(icon_frame, kMargin, kMargin); + if (highlight && alternateImage_) { + [alternateImage_ drawInRect:icon_draw_rect + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1]; + } else { + [image_ drawInRect:icon_draw_rect + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1]; + } + if (title_) { + NSAttributedString* attributes = + [[NSAttributedString alloc] initWithString:title_ + attributes:[self titleAttributes]]; + CGFloat title_width = [attributes size].width; + NSRect title_rect = NSMakeRect(kStatusItemLength, + 0, + title_width + kStatusItemLength, + [[statusItem_ statusBar] thickness]); + [title_ drawInRect:title_rect + withAttributes:[self titleAttributes]]; + [statusItem_ setLength:title_width + kStatusItemLength]; + } +} + +- (NSDictionary *)titleAttributes { + NSFont* font = [NSFont menuBarFontOfSize:0]; + NSColor* foregroundColor = [NSColor blackColor]; + + return [NSDictionary dictionaryWithObjectsAndKeys: + font, NSFontAttributeName, + foregroundColor, NSForegroundColorAttributeName, + nil]; +} + +- (void)setImage:(NSImage*)image { + image_.reset([image copy]); +} + +- (void)setAlternateImage:(NSImage*)image { + alternateImage_.reset([image copy]); +} + +- (void)setHighlight:(BOOL)highlight { + isHighlightEnable_ = highlight; +} + +-(void)setTitle:(NSString*) title { + //title_= [title retain]; + title_.reset([title copy]); +} + +- (void)setMenuController:(AtomMenuController*)menu { + menu_controller_ = menu; +} + +-(void)mouseDown:(NSEvent *)event { + inMouseEventSequence_ = YES; + [self setNeedsDisplay:YES]; +} + +-(void)mouseUp:(NSEvent *)event { + if (!inMouseEventSequence_) { + // If the menu is showing, when user clicked the tray icon, the `mouseDown` + // event will be dissmissed, we need to close the menu at this time. + [self setNeedsDisplay:YES]; + return; + } + inMouseEventSequence_ = NO; + if (event.clickCount == 1) { + if (menu_controller_) { + [statusItem_ popUpStatusItemMenu:[menu_controller_ menu]]; + } + + NSRect frame = event.window.frame; + gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); + NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; + bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); + trayIcon_->NotifyClicked(bounds); + } + + if (event.clickCount == 2 && !menu_controller_) { + trayIcon_->NotifyDoubleClicked(); + } + [self setNeedsDisplay:YES]; +} + +-(BOOL) shouldHighlight { + BOOL is_menu_open = [menu_controller_ isMenuOpen]; + return isHighlightEnable_ && (inMouseEventSequence_ || is_menu_open); } @end @@ -45,45 +152,36 @@ namespace atom { TrayIconCocoa::TrayIconCocoa() { - controller_.reset([[StatusItemController alloc] initWithIcon:this]); - - item_.reset([[[NSStatusBar systemStatusBar] - statusItemWithLength:NSVariableStatusItemLength] retain]); - [item_ setEnabled:YES]; - [item_ setTarget:controller_]; - [item_ setAction:@selector(handleClick:)]; - [item_ setDoubleAction:@selector(handleDoubleClick:)]; - [item_ setHighlightMode:YES]; + status_item_view_.reset([[StatusItemView alloc] initWithIcon:this]); } TrayIconCocoa::~TrayIconCocoa() { - // Remove the status item from the status bar. - [[NSStatusBar systemStatusBar] removeStatusItem:item_]; + [status_item_view_ removeItem]; } void TrayIconCocoa::SetImage(const gfx::Image& image) { - [item_ setImage:image.AsNSImage()]; + [status_item_view_ setImage:image.AsNSImage()]; } void TrayIconCocoa::SetPressedImage(const gfx::Image& image) { - [item_ setAlternateImage:image.AsNSImage()]; + [status_item_view_ setAlternateImage:image.AsNSImage()]; } void TrayIconCocoa::SetToolTip(const std::string& tool_tip) { - [item_ setToolTip:base::SysUTF8ToNSString(tool_tip)]; + [status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)]; } void TrayIconCocoa::SetTitle(const std::string& title) { - [item_ setTitle:base::SysUTF8ToNSString(title)]; + [status_item_view_ setTitle:base::SysUTF8ToNSString(title)]; } void TrayIconCocoa::SetHighlightMode(bool highlight) { - [item_ setHighlightMode:highlight]; + [status_item_view_ setHighlight:highlight]; } void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) { menu_.reset([[AtomMenuController alloc] initWithModel:menu_model]); - [item_ setMenu:[menu_ menu]]; + [status_item_view_ setMenuController:menu_.get()]; } // static