-
Notifications
You must be signed in to change notification settings - Fork 3
/
ASHStatusItemPopover.m
150 lines (123 loc) · 4.51 KB
/
ASHStatusItemPopover.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//
// ASHStatusItemPopover.m
// ASHStatusItemPopover
//
// Created by Adam Hartford on 12/19/13.
// Copyright (c) 2013 Adam Hartford. All rights reserved.
//
#import "ASHStatusItemPopover.h"
@interface ASHStatusItemPopover ()
{
BOOL _active;
NSStatusItem *_statusItem;
NSMenu *_dummyMenu;
id _popoverTransiencyMonitor;
}
- (void)showPopover;
- (void)hidePopover;
- (void)toggleImage;
@end
@implementation ASHStatusItemPopover
- (id)init
{
CGRect frame = CGRectMake(0, 0, STATUS_ITEM_WIDTH, [NSStatusBar systemStatusBar].thickness);
if (self = [super initWithFrame:frame]) {
// No dock icon, don't become active, don't focus.
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
_statusItem.view = self;
_popover = [[NSPopover alloc] init];
_popover.delegate = self;
// We have the status item show a dummy menu to force other menu bar apps to close.
_dummyMenu = [[NSMenu alloc] init];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
NSColor *color = _active ? [NSColor selectedMenuItemColor] : [NSColor clearColor];
[color setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
- (void)setWindowController:(NSWindowController *)windowController
{
windowController.window.level = NSPopUpMenuWindowLevel;
[windowController.window setOpaque:NO];
windowController.window.backgroundColor = [NSColor clearColor];
_windowController = windowController;
}
- (void)mouseDown:(NSEvent *)theEvent
{
if (!_active) [self showPopover];
else [self hidePopover];
[self setNeedsDisplay:YES];
}
- (void)showPopover
{
_active = YES;
[self toggleImage];
[_statusItem popUpStatusItemMenu:_dummyMenu];
// Copy the contents of the panel to an NSView
NSData *archivedView = [NSKeyedArchiver archivedDataWithRootObject:_windowController.window.contentView];
NSView *viewCopy = [NSKeyedUnarchiver unarchiveObjectWithData:archivedView];
// We want to show the NSView with the panel's contents so that the NSPopover animation looks correct.
// Once the popover is shown, we'll remove the subviews from the NSView and be left with the panel overlay.
NSViewController *viewController = [[NSViewController alloc] init];
viewCopy.window.level = NSSubmenuWindowLevel;
viewController.view = viewCopy;
_popover.contentViewController = viewController;
[_popover showRelativeToRect:self.frame ofView:self preferredEdge:NSMinYEdge];
// Close the popover if we click anywhere outside the view.
_popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask handler:^(NSEvent* event) {
[self hidePopover];
}];
}
- (void)hidePopover
{
_active = NO;
[self toggleImage];
[_windowController close];
[_popover close];
[NSEvent removeMonitor:_popoverTransiencyMonitor];
}
- (void)toggleImage
{
NSImage *img = self.image;
self.image = _alternateImage;
_alternateImage = img;
}
#pragma mark - NSPopoverDelegate methods
- (void)popoverWillShow:(NSNotification *)notification
{
if (_popoverWillShow) _popoverWillShow();
}
- (void)popoverDidShow:(NSNotification *)notification
{
// Place the panel over the middle of the NSPopover view.
CGPoint p = self.window.frame.origin;
// p.y -= _windowController.window.frame.size.height + 13;
// p.x -= (_windowController.window.frame.size.width / 2) - 11;
// OSX 10.10 fixed
p.x = _popover.contentViewController.view.window.frame.origin.x + _popover.contentViewController.view.frame.origin.x;
p.y = _popover.contentViewController.view.window.frame.origin.y + _popover.contentViewController.view.frame.origin.y;
// Bring the panel over the popover
[_windowController.window setFrameOrigin:p];
[_windowController.window makeKeyAndOrderFront:nil];
NSMutableArray *views = [NSMutableArray arrayWithArray:_popover.contentViewController.view.subviews];
for (NSView *view in views) [view removeFromSuperview];
if (_popoverDidShow) _popoverDidShow();
}
- (void)popoverWillClose:(NSNotification *)notification
{
if (_popoverWillClose) _popoverWillClose();
}
- (void)popoverDidClose:(NSNotification *)notification
{
if (_popoverDidClose) _popoverDidClose();
}
@end
#pragma mark - ASHStatusItemPanel
@implementation ASHStatusItemPanel
- (BOOL)canBecomeKeyWindow { return YES; }
@end