From e67244fe474d0ce6059eecabdc88d43340d5e136 Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Wed, 13 Nov 2019 12:12:33 -0800 Subject: [PATCH 1/5] Merge support for nested menus on macOS, from https://github.com/Arshiamidos/systray --- example/main.go | 8 +++ go.sum | 4 ++ systray.go | 46 ++++++++++++--- systray.h | 3 +- systray_darwin.m | 130 +++++++++++++++++++++++++++++++++++++----- systray_linux.c | 5 ++ systray_nonwindows.go | 25 +++++--- systray_windows.go | 1 + 8 files changed, 192 insertions(+), 30 deletions(-) diff --git a/example/main.go b/example/main.go index 5efccbd..1736b9a 100644 --- a/example/main.go +++ b/example/main.go @@ -41,6 +41,12 @@ func onReady() { mChecked := systray.AddMenuItem("Unchecked", "Check Me") mEnabled := systray.AddMenuItem("Enabled", "Enabled") systray.AddMenuItem("Ignored", "Ignored") + + subMenuTop := systray.AddMenuItem("SubMenu", "SubMenu Test (top)") + subMenuMiddle := subMenuTop.AddSubMenuItem("SubMenu - Level 2", "SubMenu Test (middle") + subMenuMiddle.AddSubMenuItem("SubMenu - Level 3", "SubMenu Test (bottom)") + subMenuBottom2 := subMenuMiddle.AddSubMenuItem("Panic!", "SubMenu Test (bottom)") + mUrl := systray.AddMenuItem("Open UI", "my home") mQuit := systray.AddMenuItem("退出", "Quit the whole app") @@ -67,6 +73,8 @@ func onReady() { mEnabled.Disable() case <-mUrl.ClickedCh: systray.ShowAppWindow("https://www.getlantern.org") + case <-subMenuBottom2.ClickedCh: + panic("panic button pressed") case <-mToggle.ClickedCh: if shown { mQuitOrig.Hide() diff --git a/go.sum b/go.sum index 9e9e928..2dc9320 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= @@ -21,10 +22,13 @@ github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8 h1:RVMGIuuNgrpGB7I79f6xfhG github.com/lxn/win v0.0.0-20190919090605-24c5960b03d8/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e h1:VAzdS5Nw68fbf5RZ8RDVlUvPXNU6Z3jtPCK/qvm4FoQ= github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13 h1:/zi0zzlPHWXYXrO1LjNRByFu8sdGgCkj2JLDdBIB84k= diff --git a/systray.go b/systray.go index 750e5f3..0a1ef36 100644 --- a/systray.go +++ b/systray.go @@ -1,13 +1,13 @@ /* -Package systray is a cross platfrom Go library to place an icon and menu in the -notification area. -Supports Windows, Mac OSX and Linux currently. +Package systray is a cross-platform Go library to place an icon and menu in the notification area. + Methods can be called from any goroutine except Run(), which should be called at the very beginning of main() to lock at main thread. */ package systray import ( + "fmt" "runtime" "sync" "sync/atomic" @@ -36,6 +36,28 @@ type MenuItem struct { disabled bool // checked menu item has a tick before the title checked bool + // parent item, for sub menus + parent *MenuItem +} + +func (item *MenuItem) String() string { + if item.parent == nil { + return fmt.Sprintf("MenuItem[%d, %q]", item.id, item.title) + } + return fmt.Sprintf("MenuItem[%d, parent %d, %q]", item.id, item.parent.id, item.title) +} + +// newMenuItem returns a populated MenuItem object +func newMenuItem(title string, tooltip string, parent *MenuItem) *MenuItem { + return &MenuItem{ + ClickedCh: make(chan struct{}), + id: atomic.AddInt32(¤tID, 1), + title: title, + tooltip: tooltip, + disabled: false, + checked: false, + parent: parent, + } } var ( @@ -97,9 +119,7 @@ func Quit() { // // It can be safely invoked from different goroutines. func AddMenuItem(title string, tooltip string) *MenuItem { - id := atomic.AddInt32(¤tID, 1) - item := &MenuItem{nil, id, title, tooltip, false, false} - item.ClickedCh = make(chan struct{}) + item := newMenuItem(title, tooltip, nil) item.update() return item } @@ -109,6 +129,16 @@ func AddSeparator() { addSeparator(atomic.AddInt32(¤tID, 1)) } +// AddSubMenuItem adds nested sub-menu item with designated title and tooltip, returning a channel +// that notifies whenever that menu item is clicked. +// +// It can be safely invoked from different goroutines. +func (item *MenuItem) AddSubMenuItem(title string, tooltip string) *MenuItem { + child := newMenuItem(title, tooltip, item) + child.update() + return child +} + // SetTitle set the text to display on a menu item func (item *MenuItem) SetTitle(title string) { item.title = title @@ -121,7 +151,7 @@ func (item *MenuItem) SetTooltip(tooltip string) { item.update() } -// Disabled checkes if the menu item is disabled +// Disabled checks if the menu item is disabled func (item *MenuItem) Disabled() bool { return item.disabled } @@ -165,7 +195,7 @@ func (item *MenuItem) Uncheck() { item.update() } -// update propogates changes on a menu item to systray +// update propagates changes on a menu item to systray func (item *MenuItem) update() { menuItemsLock.Lock() defer menuItemsLock.Unlock() diff --git a/systray.h b/systray.h index 128315f..6b92cff 100644 --- a/systray.h +++ b/systray.h @@ -10,7 +10,8 @@ void setTooltip(char* tooltip); void configureAppWindow(char* title, int width, int height); void showAppWindow(char* url); void add_or_update_menu_item(int menuId, char* title, char* tooltip, short disabled, short checked); +void add_or_update_submenu_item(int parent,int menuId, char* title, char* tooltip, short disabled, short checked); void add_separator(int menuId); void hide_menu_item(int menuId); void show_menu_item(int menuId); -void quit(); +void quit(); \ No newline at end of file diff --git a/systray_darwin.m b/systray_darwin.m index fff2465..d9c6758 100644 --- a/systray_darwin.m +++ b/systray_darwin.m @@ -48,6 +48,7 @@ @interface AppDelegate: NSObject - (void) add_or_update_menu_item:(MenuItem*) item; + - (void) add_or_update_submenu_item:(NSArray*) imageAndMenuId; - (IBAction)menuHandler:(id)sender; @property (assign) IBOutlet NSWindow *window; @end @@ -112,20 +113,21 @@ NSNumber* menuId = [sender representedObject]; systray_menu_item_selected(menuId.intValue); } - -- (void) add_or_update_menu_item:(MenuItem*) item -{ - NSMenuItem* menuItem; - int existedMenuIndex = [menu indexOfItemWithRepresentedObject: item->menuId]; - if (existedMenuIndex == -1) { - menuItem = [menu addItemWithTitle:item->title action:@selector(menuHandler:) keyEquivalent:@""]; +- (void)add_or_update_menu_item:(MenuItem *)item { + NSMenuItem *menuItem; + menuItem = find_menu_with_parent(menu, item->menuId); + if (menuItem == NULL) { + menuItem = [menu addItemWithTitle:item->title + action:@selector(menuHandler:) + keyEquivalent:@""]; [menuItem setTarget:self]; - [menuItem setRepresentedObject: item->menuId]; + [menuItem setRepresentedObject:item->menuId]; + [menuItem setTag:[item->menuId integerValue]]; - } - else { - menuItem = [menu itemAtIndex: existedMenuIndex]; + } else { [menuItem setTitle:item->title]; + [menuItem setTag:[item->menuId integerValue]]; + [menuItem setTarget:self]; } [menuItem setToolTip:item->tooltip]; if (item->disabled == 1) { @@ -140,6 +142,94 @@ } } +NSMenuItem *find_menu_with_parent(NSMenu *ourMenu, NSNumber *parent) { + NSMenuItem *foundItem = [ourMenu itemWithTag:[parent integerValue]]; + if (foundItem == NULL) { + NSArray *menu_items = ourMenu.itemArray; + int i; + for (i = 0; i < [menu_items count]; i++) { + NSMenuItem *i_item = [menu_items objectAtIndex:i]; + if (i_item.hasSubmenu) { + NSMenuItem *foundItem2 = find_menu_with_parent(i_item.submenu, parent); + if (foundItem2 == NULL) { + + } else { + foundItem = foundItem2; + break; + } + } + } + return foundItem; + } else { + return foundItem; + } +}; + +- (void)add_or_update_submenu_item:(NSArray *)imageAndMenuId { + + NSNumber *parent = [imageAndMenuId objectAtIndex:0]; + MenuItem *newItem = [imageAndMenuId objectAtIndex:1]; + + NSMenuItem *foundItem = find_menu_with_parent(menu, parent); + + if (foundItem == NULL) { + NSLog(@"%s", ">>> foundItem == NULL - this should not occur!"); + } + + if (foundItem.hasSubmenu) { + NSMenu *oldMenu = foundItem.submenu; + + NSMenuItem *tempItem = [oldMenu addItemWithTitle:newItem->title + action:@selector(menuHandler:) + keyEquivalent:@""]; + tempItem.tag = [newItem->menuId integerValue]; + tempItem.title = newItem->title; + tempItem.action = @selector(menuHandler:); + tempItem.target = self; + tempItem.representedObject = newItem->menuId; + + //[oldMenu addItem:tempItem]; + if (newItem->disabled == 1) { + tempItem.enabled = FALSE; + } else { + tempItem.enabled = TRUE; + } + if (newItem->checked == 1) { + tempItem.state = NSControlStateValueOn; + } else { + tempItem.state = NSControlStateValueOff; + } + [foundItem setSubmenu:oldMenu]; + } else { + + NSMenu *newMenu = [[NSMenu alloc] init]; + NSMenuItem *tempItem = [newMenu addItemWithTitle:newItem->title + action:@selector(menuHandler:) + keyEquivalent:@""]; + + tempItem.tag = [newItem->menuId integerValue]; + tempItem.title = newItem->title; + tempItem.toolTip = newItem->tooltip; + tempItem.representedObject = newItem->menuId; + + [tempItem setTarget:self]; + + if (newItem->disabled == 1) { + tempItem.enabled = FALSE; + } else { + tempItem.enabled = TRUE; + } + if (newItem->checked == 1) { + tempItem.state = NSControlStateValueOn; + } else { + tempItem.state = NSControlStateValueOff; + } + [foundItem setSubmenu:newMenu]; + } +} + + + - (void) add_separator:(NSNumber*) menuId { [menu addItem: [NSMenuItem separatorItem]]; @@ -161,11 +251,10 @@ NSNumber* menuId = [imageAndMenuId objectAtIndex:1]; NSMenuItem* menuItem; - int existedMenuIndex = [menu indexOfItemWithRepresentedObject: menuId]; - if (existedMenuIndex == -1) { + menuItem = find_menu_with_parent(menu, menuId); + if (menuItem == NULL) { return; } - menuItem = [menu itemAtIndex: existedMenuIndex]; menuItem.image = image; } @@ -243,6 +332,19 @@ void add_or_update_menu_item(int menuId, char* title, char* tooltip, short disab runInMainThread(@selector(add_or_update_menu_item:), (id)item); } +void add_or_update_submenu_item(int parent, int menuId, char *title, + char *tooltip, short disabled, short checked) { + MenuItem *item = [[MenuItem alloc] initWithId:menuId + withTitle:title + withTooltip:tooltip + withDisabled:disabled + withChecked:checked]; + free(title); + free(tooltip); + NSNumber *parent2 = [NSNumber numberWithInt:parent]; + runInMainThread(@selector(add_or_update_submenu_item:), + @[ parent2, (id)item ]); +} void add_separator(int menuId) { NSNumber *mId = [NSNumber numberWithInt:menuId]; runInMainThread(@selector(add_separator:), (id)mId); diff --git a/systray_linux.c b/systray_linux.c index 7b045f2..eddb194 100644 --- a/systray_linux.c +++ b/systray_linux.c @@ -199,6 +199,11 @@ void add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disa g_idle_add(do_add_or_update_menu_item, mii); } +void add_or_update_submenu_item(int parent_id, int menu_id, char* title, char* tooltip, short disabled, short checked) { + // TODO: add support for sub-menus + add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disabled, short checked) ; +} + void add_separator(int menu_id) { MenuItemInfo *mii = malloc(sizeof(MenuItemInfo)); mii->menu_id = menu_id; diff --git a/systray_nonwindows.go b/systray_nonwindows.go index 598df9e..74e0a35 100644 --- a/systray_nonwindows.go +++ b/systray_nonwindows.go @@ -58,13 +58,24 @@ func addOrUpdateMenuItem(item *MenuItem) { if item.checked { checked = 1 } - C.add_or_update_menu_item( - C.int(item.id), - C.CString(item.title), - C.CString(item.tooltip), - disabled, - checked, - ) + if item.parent == nil { + C.add_or_update_menu_item( + C.int(item.id), + C.CString(item.title), + C.CString(item.tooltip), + disabled, + checked, + ) + } else { + C.add_or_update_submenu_item( + C.int(item.parent.id), + C.int(item.id), + C.CString(item.title), + C.CString(item.tooltip), + disabled, + checked, + ) + } } // SetIcon sets the icon of a menu item. Only available on Mac. diff --git a/systray_windows.go b/systray_windows.go index 4f8e731..5ec0367 100644 --- a/systray_windows.go +++ b/systray_windows.go @@ -144,6 +144,7 @@ func addOrUpdateMenuItem(item *MenuItem) { if err != nil { fail("Unable to set menu item enabled", err) } + // TODO: add support for sub-menus } func (item *MenuItem) SetIcon(iconBytes []byte) { From c2d9dd1b1352dbe6ce39fc98742a145c481cc8af Mon Sep 17 00:00:00 2001 From: Thomas Stromberg Date: Wed, 13 Nov 2019 12:18:18 -0800 Subject: [PATCH 2/5] Fix missing paren in tooltip --- example/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/main.go b/example/main.go index 1736b9a..c08caea 100644 --- a/example/main.go +++ b/example/main.go @@ -43,7 +43,7 @@ func onReady() { systray.AddMenuItem("Ignored", "Ignored") subMenuTop := systray.AddMenuItem("SubMenu", "SubMenu Test (top)") - subMenuMiddle := subMenuTop.AddSubMenuItem("SubMenu - Level 2", "SubMenu Test (middle") + subMenuMiddle := subMenuTop.AddSubMenuItem("SubMenu - Level 2", "SubMenu Test (middle)") subMenuMiddle.AddSubMenuItem("SubMenu - Level 3", "SubMenu Test (bottom)") subMenuBottom2 := subMenuMiddle.AddSubMenuItem("Panic!", "SubMenu Test (bottom)") From 4ffb76469cb002b13d595d9804a45e407455f980 Mon Sep 17 00:00:00 2001 From: Joesis Date: Wed, 4 Dec 2019 18:32:12 -0800 Subject: [PATCH 3/5] change the example to demo the bug of nested menus in macOS --- example/main.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/example/main.go b/example/main.go index c08caea..6673266 100644 --- a/example/main.go +++ b/example/main.go @@ -44,7 +44,7 @@ func onReady() { subMenuTop := systray.AddMenuItem("SubMenu", "SubMenu Test (top)") subMenuMiddle := subMenuTop.AddSubMenuItem("SubMenu - Level 2", "SubMenu Test (middle)") - subMenuMiddle.AddSubMenuItem("SubMenu - Level 3", "SubMenu Test (bottom)") + subMenuBottom := subMenuMiddle.AddSubMenuItem("SubMenu - Level 3", "SubMenu Test (bottom)") subMenuBottom2 := subMenuMiddle.AddSubMenuItem("Panic!", "SubMenu Test (bottom)") mUrl := systray.AddMenuItem("Open UI", "my home") @@ -56,6 +56,20 @@ func onReady() { systray.AddSeparator() mToggle := systray.AddMenuItem("Toggle", "Toggle the Quit button") shown := true + toggle := func() { + if shown { + subMenuBottom.Uncheck() + mQuitOrig.Hide() + mEnabled.Hide() + shown = false + } else { + subMenuBottom.Check() + mQuitOrig.Show() + mEnabled.Show() + shown = true + } + } + for { select { case <-mChange.ClickedCh: @@ -75,16 +89,10 @@ func onReady() { systray.ShowAppWindow("https://www.getlantern.org") case <-subMenuBottom2.ClickedCh: panic("panic button pressed") + case <-subMenuBottom.ClickedCh: + toggle() case <-mToggle.ClickedCh: - if shown { - mQuitOrig.Hide() - mEnabled.Hide() - shown = false - } else { - mQuitOrig.Show() - mEnabled.Show() - shown = true - } + toggle() case <-mQuit.ClickedCh: systray.Quit() fmt.Println("Quit2 now...") From 707d447c8780039aa2c64dcd9732813952d4fc3d Mon Sep 17 00:00:00 2001 From: Ox Cart Date: Fri, 13 Mar 2020 09:39:40 -0500 Subject: [PATCH 4/5] Simplified sub-menu support on MacOS --- example/main.go | 4 +- systray.h | 3 +- systray_darwin.m | 151 ++++++++++++------------------------------ systray_linux.c | 8 +-- systray_nonwindows.go | 28 +++----- 5 files changed, 57 insertions(+), 137 deletions(-) diff --git a/example/main.go b/example/main.go index c67983b..dd793e4 100644 --- a/example/main.go +++ b/example/main.go @@ -61,12 +61,12 @@ func onReady() { shown := true toggle := func() { if shown { - subMenuBottom.Uncheck() + subMenuBottom.Check() mQuitOrig.Hide() mEnabled.Hide() shown = false } else { - subMenuBottom.Check() + subMenuBottom.Uncheck() mQuitOrig.Show() mEnabled.Show() shown = true diff --git a/systray.h b/systray.h index 4bf1dac..c064e4a 100644 --- a/systray.h +++ b/systray.h @@ -11,8 +11,7 @@ void setTitle(char* title); void setTooltip(char* tooltip); void configureAppWindow(char* title, int width, int height); void showAppWindow(char* url); -void add_or_update_menu_item(int menuId, char* title, char* tooltip, short disabled, short checked); -void add_or_update_submenu_item(int parent,int menuId, char* title, char* tooltip, short disabled, short checked); +void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked); void add_separator(int menuId); void hide_menu_item(int menuId); void show_menu_item(int menuId); diff --git a/systray_darwin.m b/systray_darwin.m index dd5fd38..cf15282 100644 --- a/systray_darwin.m +++ b/systray_darwin.m @@ -17,12 +17,14 @@ { @public NSNumber* menuId; + NSNumber* parentMenuId; NSString* title; NSString* tooltip; short disabled; short checked; } -(id) initWithId: (int)theMenuId +withParentMenuId: (int)theParentMenuId withTitle: (const char*)theTitle withTooltip: (const char*)theTooltip withDisabled: (short)theDisabled @@ -30,12 +32,14 @@ @end @implementation MenuItem -(id) initWithId: (int)theMenuId + withParentMenuId: (int)theParentMenuId withTitle: (const char*)theTitle withTooltip: (const char*)theTooltip withDisabled: (short)theDisabled withChecked: (short)theChecked { menuId = [NSNumber numberWithInt:theMenuId]; + parentMenuId = [NSNumber numberWithInt:theParentMenuId]; title = [[NSString alloc] initWithCString:theTitle encoding:NSUTF8StringEncoding]; tooltip = [[NSString alloc] initWithCString:theTooltip @@ -48,7 +52,6 @@ @interface AppDelegate: NSObject - (void) add_or_update_menu_item:(MenuItem*) item; - - (void) add_or_update_submenu_item:(NSArray*) imageAndMenuId; - (IBAction)menuHandler:(id)sender; @property (assign) IBOutlet NSWindow *window; @end @@ -113,22 +116,31 @@ NSNumber* menuId = [sender representedObject]; systray_menu_item_selected(menuId.intValue); } + - (void)add_or_update_menu_item:(MenuItem *)item { + NSMenu *theMenu = self->menu; + NSMenuItem *parentItem; + if ([item->parentMenuId integerValue] > 0) { + parentItem = find_menu_item(menu, item->parentMenuId); + if (parentItem.hasSubmenu) { + theMenu = parentItem.submenu; + } else { + theMenu = [[NSMenu alloc] init]; + [parentItem setSubmenu:theMenu]; + } + } + NSMenuItem *menuItem; - menuItem = find_menu_with_parent(menu, item->menuId); + menuItem = find_menu_item(theMenu, item->menuId); if (menuItem == NULL) { - menuItem = [menu addItemWithTitle:item->title + menuItem = [theMenu addItemWithTitle:item->title action:@selector(menuHandler:) keyEquivalent:@""]; - [menuItem setTarget:self]; [menuItem setRepresentedObject:item->menuId]; - [menuItem setTag:[item->menuId integerValue]]; - - } else { - [menuItem setTitle:item->title]; - [menuItem setTag:[item->menuId integerValue]]; - [menuItem setTarget:self]; } + [menuItem setTitle:item->title]; + [menuItem setTag:[item->menuId integerValue]]; + [menuItem setTarget:self]; [menuItem setToolTip:item->tooltip]; if (item->disabled == 1) { menuItem.enabled = FALSE; @@ -142,94 +154,26 @@ } } -NSMenuItem *find_menu_with_parent(NSMenu *ourMenu, NSNumber *parent) { - NSMenuItem *foundItem = [ourMenu itemWithTag:[parent integerValue]]; - if (foundItem == NULL) { - NSArray *menu_items = ourMenu.itemArray; - int i; - for (i = 0; i < [menu_items count]; i++) { - NSMenuItem *i_item = [menu_items objectAtIndex:i]; - if (i_item.hasSubmenu) { - NSMenuItem *foundItem2 = find_menu_with_parent(i_item.submenu, parent); - if (foundItem2 == NULL) { - - } else { - foundItem = foundItem2; - break; - } +NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) { + NSMenuItem *foundItem = [ourMenu itemWithTag:[menuId integerValue]]; + if (foundItem != NULL) { + return foundItem; + } + NSArray *menu_items = ourMenu.itemArray; + int i; + for (i = 0; i < [menu_items count]; i++) { + NSMenuItem *i_item = [menu_items objectAtIndex:i]; + if (i_item.hasSubmenu) { + foundItem = find_menu_item(i_item.submenu, menuId); + if (foundItem != NULL) { + return foundItem; } } - return foundItem; - } else { - return foundItem; } + + return NULL; }; -- (void)add_or_update_submenu_item:(NSArray *)imageAndMenuId { - - NSNumber *parent = [imageAndMenuId objectAtIndex:0]; - MenuItem *newItem = [imageAndMenuId objectAtIndex:1]; - - NSMenuItem *foundItem = find_menu_with_parent(menu, parent); - - if (foundItem == NULL) { - NSLog(@"%s", ">>> foundItem == NULL - this should not occur!"); - } - - if (foundItem.hasSubmenu) { - NSMenu *oldMenu = foundItem.submenu; - - NSMenuItem *tempItem = [oldMenu addItemWithTitle:newItem->title - action:@selector(menuHandler:) - keyEquivalent:@""]; - tempItem.tag = [newItem->menuId integerValue]; - tempItem.title = newItem->title; - tempItem.action = @selector(menuHandler:); - tempItem.target = self; - tempItem.representedObject = newItem->menuId; - - //[oldMenu addItem:tempItem]; - if (newItem->disabled == 1) { - tempItem.enabled = FALSE; - } else { - tempItem.enabled = TRUE; - } - if (newItem->checked == 1) { - tempItem.state = NSControlStateValueOn; - } else { - tempItem.state = NSControlStateValueOff; - } - [foundItem setSubmenu:oldMenu]; - } else { - - NSMenu *newMenu = [[NSMenu alloc] init]; - NSMenuItem *tempItem = [newMenu addItemWithTitle:newItem->title - action:@selector(menuHandler:) - keyEquivalent:@""]; - - tempItem.tag = [newItem->menuId integerValue]; - tempItem.title = newItem->title; - tempItem.toolTip = newItem->tooltip; - tempItem.representedObject = newItem->menuId; - - [tempItem setTarget:self]; - - if (newItem->disabled == 1) { - tempItem.enabled = FALSE; - } else { - tempItem.enabled = TRUE; - } - if (newItem->checked == 1) { - tempItem.state = NSControlStateValueOn; - } else { - tempItem.state = NSControlStateValueOff; - } - [foundItem setSubmenu:newMenu]; - } -} - - - - (void) add_separator:(NSNumber*) menuId { [menu addItem: [NSMenuItem separatorItem]]; @@ -246,12 +190,12 @@ NSMenuItem *find_menu_with_parent(NSMenu *ourMenu, NSNumber *parent) { [menuItem setHidden:TRUE]; } -- (void)setMenuItemIcon:(NSArray*)imageAndMenuId { +- (void) setMenuItemIcon:(NSArray*)imageAndMenuId { NSImage* image = [imageAndMenuId objectAtIndex:0]; NSNumber* menuId = [imageAndMenuId objectAtIndex:1]; NSMenuItem* menuItem; - menuItem = find_menu_with_parent(menu, menuId); + menuItem = find_menu_item(menu, menuId); if (menuItem == NULL) { return; } @@ -324,26 +268,13 @@ void setTooltip(char* ctooltip) { runInMainThread(@selector(setTooltip:), (id)tooltip); } -void add_or_update_menu_item(int menuId, char* title, char* tooltip, short disabled, short checked) { - MenuItem* item = [[MenuItem alloc] initWithId: menuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked]; +void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked) { + MenuItem* item = [[MenuItem alloc] initWithId: menuId withParentMenuId: parentMenuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked]; free(title); free(tooltip); runInMainThread(@selector(add_or_update_menu_item:), (id)item); } -void add_or_update_submenu_item(int parent, int menuId, char *title, - char *tooltip, short disabled, short checked) { - MenuItem *item = [[MenuItem alloc] initWithId:menuId - withTitle:title - withTooltip:tooltip - withDisabled:disabled - withChecked:checked]; - free(title); - free(tooltip); - NSNumber *parent2 = [NSNumber numberWithInt:parent]; - runInMainThread(@selector(add_or_update_submenu_item:), - @[ parent2, (id)item ]); -} void add_separator(int menuId) { NSNumber *mId = [NSNumber numberWithInt:menuId]; runInMainThread(@selector(add_separator:), (id)mId); diff --git a/systray_linux.c b/systray_linux.c index 025f7d8..50ff62f 100644 --- a/systray_linux.c +++ b/systray_linux.c @@ -189,7 +189,8 @@ void setTooltip(char* ctooltip) { void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) { } -void add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disabled, short checked) { +void add_or_update_menu_item(int menu_id, int parent_menu_id, char* title, char* tooltip, short disabled, short checked) { + // TODO: add support for sub-menus MenuItemInfo *mii = malloc(sizeof(MenuItemInfo)); mii->menu_id = menu_id; mii->title = title; @@ -199,11 +200,6 @@ void add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disa g_idle_add(do_add_or_update_menu_item, mii); } -void add_or_update_submenu_item(int parent_id, int menu_id, char* title, char* tooltip, short disabled, short checked) { - // TODO: add support for sub-menus - add_or_update_menu_item(int menu_id, char* title, char* tooltip, short disabled, short checked) ; -} - void add_separator(int menu_id) { MenuItemInfo *mii = malloc(sizeof(MenuItemInfo)); mii->menu_id = menu_id; diff --git a/systray_nonwindows.go b/systray_nonwindows.go index 9e90e6b..b564879 100644 --- a/systray_nonwindows.go +++ b/systray_nonwindows.go @@ -58,24 +58,18 @@ func addOrUpdateMenuItem(item *MenuItem) { if item.checked { checked = 1 } - if item.parent == nil { - C.add_or_update_menu_item( - C.int(item.id), - C.CString(item.title), - C.CString(item.tooltip), - disabled, - checked, - ) - } else { - C.add_or_update_submenu_item( - C.int(item.parent.id), - C.int(item.id), - C.CString(item.title), - C.CString(item.tooltip), - disabled, - checked, - ) + var parentID int32 = 0 + if item.parent != nil { + parentID = item.parent.id } + C.add_or_update_menu_item( + C.int(item.id), + C.int(parentID), + C.CString(item.title), + C.CString(item.tooltip), + disabled, + checked, + ) } func addSeparator(id int32) { From 5b4346dabe5db16fdd71561b2b9fc80c4c686f57 Mon Sep 17 00:00:00 2001 From: Ox Cart Date: Mon, 16 Mar 2020 07:25:48 -0500 Subject: [PATCH 5/5] Code review updates --- example/main.go | 2 ++ systray.go | 6 ++---- systray_darwin.m | 18 ++++++------------ 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/example/main.go b/example/main.go index dd793e4..2a9f78c 100644 --- a/example/main.go +++ b/example/main.go @@ -62,11 +62,13 @@ func onReady() { toggle := func() { if shown { subMenuBottom.Check() + subMenuBottom2.Hide() mQuitOrig.Hide() mEnabled.Hide() shown = false } else { subMenuBottom.Uncheck() + subMenuBottom2.Show() mQuitOrig.Show() mEnabled.Show() shown = true diff --git a/systray.go b/systray.go index 0a1ef36..9e84eaa 100644 --- a/systray.go +++ b/systray.go @@ -114,8 +114,7 @@ func Quit() { } } -// AddMenuItem adds menu item with designated title and tooltip, returning a channel -// that notifies whenever that menu item is clicked. +// AddMenuItem adds a menu item with the designated title and tooltip. // // It can be safely invoked from different goroutines. func AddMenuItem(title string, tooltip string) *MenuItem { @@ -129,8 +128,7 @@ func AddSeparator() { addSeparator(atomic.AddInt32(¤tID, 1)) } -// AddSubMenuItem adds nested sub-menu item with designated title and tooltip, returning a channel -// that notifies whenever that menu item is clicked. +// AddSubMenuItem adds a nested sub-menu item with the designated title and tooltip. // // It can be safely invoked from different goroutines. func (item *MenuItem) AddSubMenuItem(title string, tooltip string) *MenuItem { diff --git a/systray_darwin.m b/systray_darwin.m index cf15282..bd14dcc 100644 --- a/systray_darwin.m +++ b/systray_darwin.m @@ -181,13 +181,10 @@ NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) { - (void) hide_menu_item:(NSNumber*) menuId { - NSMenuItem* menuItem; - int existedMenuIndex = [menu indexOfItemWithRepresentedObject: menuId]; - if (existedMenuIndex == -1) { - return; + NSMenuItem* menuItem = find_menu_item(menu, menuId); + if (menuItem != NULL) { + [menuItem setHidden:TRUE]; } - menuItem = [menu itemAtIndex: existedMenuIndex]; - [menuItem setHidden:TRUE]; } - (void) setMenuItemIcon:(NSArray*)imageAndMenuId { @@ -204,13 +201,10 @@ NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) { - (void) show_menu_item:(NSNumber*) menuId { - NSMenuItem* menuItem; - int existedMenuIndex = [menu indexOfItemWithRepresentedObject: menuId]; - if (existedMenuIndex == -1) { - return; + NSMenuItem* menuItem = find_menu_item(menu, menuId); + if (menuItem != NULL) { + [menuItem setHidden:FALSE]; } - menuItem = [menu itemAtIndex: existedMenuIndex]; - [menuItem setHidden:FALSE]; } - (void) quit