v / examples / macos_tray / tray.m
131 lines · 109 sloc · 3.99 KB · c1502b3c1fe8feaaf0a65eb04ea93cfea314a8c2
Raw
1// #include <Cocoa/Cocoa.h> // Uncomment for C/C++ intellisense
2
3// See this tutorial to learn about NSApplicationDelegate:
4// https://bumbershootsoft.wordpress.com/2018/11/22/unfiltered-cocoa-completing-the-application/
5
6static NSString *nsstring(string s) {
7 return [[NSString alloc] initWithBytesNoCopy:s.str
8 length:s.len
9 encoding:NSUTF8StringEncoding
10 freeWhenDone:NO];
11}
12
13// Manages the app lifecycle.
14@interface AppDelegate : NSObject <NSApplicationDelegate> {
15 NSAutoreleasePool *pool;
16 NSApplication *app;
17 NSStatusItem *statusItem; // our button
18 main__TrayParams trayParams; // TrayParams is defined in tray.v
19}
20@end
21
22@implementation AppDelegate
23- (AppDelegate *)initWithParams:(main__TrayParams)params {
24 if (self = [super init]) {
25 trayParams = params;
26 }
27 return self;
28}
29
30// Called when NSMenuItem is clicked.
31- (void)onAction:(id)sender {
32 struct main__TrayMenuItem *item =
33 (struct main__TrayMenuItem *)[[sender representedObject] pointerValue];
34 if (item) {
35 trayParams.on_click(*item);
36 }
37}
38
39- (NSMenu *)buildMenu {
40 NSMenu *menu = [NSMenu new];
41 [menu autorelease];
42 [menu setAutoenablesItems:NO];
43
44 main__TrayMenuItem *params_items = trayParams.items.data;
45 for (int i = 0; i < trayParams.items.len; i++) {
46 NSString *title = nsstring(params_items[i].text);
47 NSMenuItem *item = [menu addItemWithTitle:title
48 action:@selector(onAction:)
49 keyEquivalent:@""];
50 NSValue *representedObject = [NSValue valueWithPointer:(params_items + i)];
51 [item setRepresentedObject:representedObject];
52 [item setTarget:self];
53 [item autorelease];
54 [item setEnabled:YES];
55 }
56
57 return menu;
58}
59
60- (void)initTrayMenuItem {
61 NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
62 statusItem = [statusBar statusItemWithLength:NSSquareStatusItemLength];
63 [statusItem retain];
64 [statusItem setVisible:YES];
65 NSStatusBarButton *statusBarButton = [statusItem button];
66
67 // Height must be 22px.
68 NSImage *img = [NSImage imageNamed:@"icon.png"];
69 [statusBarButton setImage:img];
70 NSMenu *menu = [self buildMenu];
71 [statusItem setMenu:menu];
72}
73
74- (void)applicationWillFinishLaunching:(NSNotification *)notification {
75 NSLog(@"applicationWillFinishLaunching called");
76}
77
78- (void)applicationWillTerminate:(NSNotification *)notif;
79{ NSLog(@"applicationWillTerminate called"); }
80
81- (NSApplicationTerminateReply)applicationShouldTerminate:
82 (NSApplication *)sender {
83 NSLog(@"applicationShouldTerminate called");
84 return NSTerminateNow;
85}
86@end
87
88// Initializes NSApplication and NSStatusItem, aka system tray menu item.
89main__TrayInfo *tray_app_init(main__TrayParams params) {
90 NSApplication *app = [NSApplication sharedApplication];
91 AppDelegate *appDelegate = [[AppDelegate alloc] initWithParams:params];
92
93 // Hide icon from the doc.
94 [app setActivationPolicy:NSApplicationActivationPolicyProhibited];
95 [app setDelegate:appDelegate];
96
97 [appDelegate initTrayMenuItem];
98
99 main__TrayInfo *tray_info = malloc(sizeof(main__TrayInfo));
100 tray_info->app = app;
101 tray_info->app_delegate = appDelegate;
102 return tray_info;
103}
104
105// Blocks and runs the application.
106void tray_app_run(main__TrayInfo *tray_info) {
107 NSApplication *app = (NSApplication *)(tray_info->app);
108 [app run];
109}
110
111// Processes a single NSEvent while blocking the thread
112// until there is an event.
113void tray_app_loop(main__TrayInfo *tray_info) {
114 NSDate *until = [NSDate distantFuture];
115
116 NSApplication *app = (NSApplication *)(tray_info->app);
117 NSEvent *event = [app nextEventMatchingMask:ULONG_MAX
118 untilDate:until
119 inMode:@"kCFRunLoopDefaultMode"
120 dequeue:YES];
121
122 if (event) {
123 [app sendEvent:event];
124 }
125}
126
127// Terminates the app.
128void tray_app_exit(main__TrayInfo *tray_info) {
129 NSApplication *app = (NSApplication *)(tray_info->app);
130 [app terminate:app];
131}
132