Skip to content

Commit 0f99d4d

Browse files
authored
Merge pull request getlantern#82 from clintharrison/clint/allow-removal-on-maos
[macOS] Allow setting status item interactive removal behavior
2 parents 326befe + d4b6a8d commit 0f99d4d

File tree

7 files changed

+58
-0
lines changed

7 files changed

+58
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ If bundling manually, you may want to add one or both of the following to your I
135135

136136
Consult the [Official Apple Documentation here](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1).
137137

138+
On macOS, it's possible to set the underlying
139+
[`NSStatusItemBehavior`](https://developer.apple.com/documentation/appkit/nsstatusitembehavior?language=objc)
140+
with `systray.SetRemovalAllowed(true)`. When enabled, the user can cmd-drag the
141+
icon off the menu bar.
142+
138143
## Credits
139144

140145
- https://github.com/getlantern/systray

example/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func onReady() {
4343
trayOpenedCount := 0
4444
mOpenedCount := systray.AddMenuItem("Tray opened count", "Tray opened count")
4545
mChange := systray.AddMenuItem("Change Me", "Change Me")
46+
mAllowRemoval := systray.AddMenuItem("Allow removal", "macOS only: allow removal of the icon when cmd is pressed")
4647
mChecked := systray.AddMenuItemCheckbox("Checked", "Check Me", true)
4748
mEnabled := systray.AddMenuItem("Enabled", "Enabled")
4849
// Sets the icon of a menu item. Only available on Mac.
@@ -86,6 +87,13 @@ func onReady() {
8687
mChecked.Check()
8788
mChecked.SetTitle("Checked")
8889
}
90+
case <-mAllowRemoval.ClickedCh:
91+
systray.SetRemovalAllowed(true)
92+
go func() {
93+
time.Sleep(5 * time.Second)
94+
fmt.Printf("Time's up! setting back to no-removal-allowed on macOS.\n")
95+
systray.SetRemovalAllowed(false)
96+
}()
8997
case <-mEnabled.ClickedCh:
9098
mEnabled.SetTitle("Disabled")
9199
mEnabled.Disable()

systray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ void setIcon(const char* iconBytes, int length, bool template);
1313
void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template);
1414
void setTitle(char* title);
1515
void setTooltip(char* tooltip);
16+
void setRemovalAllowed(bool allowed);
1617
void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable);
1718
void add_separator(int menuId, int parentId);
1819
void hide_menu_item(int menuId);

systray_darwin.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes
4242
C.setMenuItemIcon(cstr, (C.int)(len(templateIconBytes)), C.int(item.id), true)
4343
}
4444

45+
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
46+
// This is only supported on macOS.
47+
func SetRemovalAllowed(allowed bool) {
48+
C.setRemovalAllowed((C.bool)(allowed))
49+
}
50+
4551
func registerSystray() {
4652
C.registerSystray()
4753
}

systray_darwin.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
7676
self->menu.delegate = self;
7777
self->menu.autoenablesItems = FALSE;
7878
self->statusItem.menu = self->menu;
79+
// Once the user has removed it, the item needs to be explicitly brought back,
80+
// even restarting the application is insufficient.
81+
// Since the interface from Go is relatively simple, for now we ensure it's
82+
// always visible at application startup.
83+
self->statusItem.visible = TRUE;
7984
systray_ready();
8085
}
8186

@@ -84,6 +89,21 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification
8489
systray_on_exit();
8590
}
8691

92+
- (void)setRemovalAllowed {
93+
NSStatusItemBehavior behavior = [self->statusItem behavior];
94+
behavior |= NSStatusItemBehaviorRemovalAllowed;
95+
self->statusItem.behavior = behavior;
96+
}
97+
98+
- (void)setRemovalForbidden {
99+
NSStatusItemBehavior behavior = [self->statusItem behavior];
100+
behavior &= ~NSStatusItemBehaviorRemovalAllowed;
101+
// Ensure the menu item is visible if it was removed, since we're now
102+
// disallowing removal.
103+
self->statusItem.visible = TRUE;
104+
self->statusItem.behavior = behavior;
105+
}
106+
87107
- (void)setIcon:(NSImage *)image {
88108
statusItem.button.image = image;
89109
[self updateTitleButtonStyle];
@@ -336,6 +356,14 @@ void setTooltip(char* ctooltip) {
336356
runInMainThread(@selector(setTooltip:), (id)tooltip);
337357
}
338358

359+
void setRemovalAllowed(bool allowed) {
360+
if (allowed) {
361+
runInMainThread(@selector(setRemovalAllowed), nil);
362+
} else {
363+
runInMainThread(@selector(setRemovalForbidden), nil);
364+
}
365+
}
366+
339367
void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable) {
340368
MenuItem* item = [[MenuItem alloc] initWithId: menuId withParentMenuId: parentMenuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked];
341369
free(title);

systray_unix.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes
134134
item.SetIcon(regularIconBytes)
135135
}
136136

137+
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
138+
// This is only supported on macOS.
139+
func SetRemovalAllowed(allowed bool) {
140+
}
141+
137142
func setInternalLoop(_ bool) {
138143
// nothing to action on Linux
139144
}

systray_windows.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,11 @@ func (t *winTray) convertToSubMenu(menuItemId uint32) (windows.Handle, error) {
541541
return menu, nil
542542
}
543543

544+
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
545+
// This is only supported on macOS.
546+
func SetRemovalAllowed(allowed bool) {
547+
}
548+
544549
func (t *winTray) addOrUpdateMenuItem(menuItemId uint32, parentId uint32, title string, disabled, checked bool) error {
545550
if !wt.isReady() {
546551
return ErrTrayNotReadyYet

0 commit comments

Comments
 (0)