diff --git a/v2/internal/ffenestri/ffenestri.h b/v2/internal/ffenestri/ffenestri.h index 5e6479f01..f89444a95 100644 --- a/v2/internal/ffenestri/ffenestri.h +++ b/v2/internal/ffenestri/ffenestri.h @@ -33,7 +33,7 @@ extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *fi extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories); extern void MessageDialog(void *appPointer, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton); extern void DarkModeEnabled(void *appPointer, char *callbackID); -extern void UpdateMenu(void *app, char *menuAsJSON); +extern void SetApplicationMenu(void *, const char *); extern void UpdateTray(void *app, char *menuAsJSON); extern void UpdateContextMenus(void *app, char *contextMenusAsJSON); extern void UpdateTrayLabel(void *app, const char *label); diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go index df96c75b1..f516230a1 100644 --- a/v2/internal/ffenestri/ffenestri_client.go +++ b/v2/internal/ffenestri/ffenestri_client.go @@ -186,20 +186,8 @@ func (c *Client) DarkModeEnabled(callbackID string) { C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID)) } -func (c *Client) UpdateMenu(menu *menu.Menu) { - - // Guard against nil menus - if menu == nil { - return - } - // Process the menu - processedMenu := NewProcessedMenu(menu) - menuJSON, err := json.Marshal(processedMenu) - if err != nil { - c.app.logger.Error("Error processing updated Menu: %s", err.Error()) - return - } - C.UpdateMenu(c.app.app, c.app.string2CString(string(menuJSON))) +func (c *Client) UpdateMenu(menuJSON string) { + C.SetApplicationMenu(c.app.app, c.app.string2CString(menuJSON)) } func (c *Client) UpdateTray(menu *menu.Menu) { diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index 2afb50345..c8c227440 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -485,68 +485,6 @@ void allocateTrayHashMaps(struct Application *app) { } } -void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) { - // Setup main application struct - struct Application *result = malloc(sizeof(struct Application)); - result->title = title; - result->width = width; - result->height = height; - result->minWidth = 0; - result->minHeight = 0; - result->maxWidth = 0; - result->maxHeight = 0; - result->resizable = resizable; - result->devtools = devtools; - result->fullscreen = fullscreen; - result->maximised = 0; - result->startHidden = startHidden; - result->decorations = 0; - result->logLevel = logLevel; - - result->mainWindow = NULL; - result->mouseEvent = NULL; - result->mouseDownMonitor = NULL; - result->mouseUpMonitor = NULL; - - // Features - result->frame = 1; - result->hideTitle = 0; - result->hideTitleBar = 0; - result->fullSizeContent = 0; - result->useToolBar = 0; - result->hideToolbarSeparator = 0; - result->appearance = NULL; - result->windowBackgroundIsTranslucent = 0; - - // Window data - result->delegate = NULL; - - // Menu - result->applicationMenu = NULL; - - // Tray - result->trayMenuAsJSON = NULL; - result->trayLabel = NULL; - result->trayIconName = NULL; - result->trayIconPosition = NSImageLeft; // Left of the text by default - result->processedTrayMenu = NULL; - result->statusItem = NULL; - - // Context Menus - result->contextMenuStore = NULL; - result->contextMenusAsJSON = NULL; - result->processedContextMenus = NULL; - contextMenuData = NULL; - - // Window Appearance - result->titlebarAppearsTransparent = 0; - result->webviewIsTranparent = 0; - - result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback; - - return (void*) result; -} - int releaseNSObject(void *const context, struct hashmap_element_s *const e) { msg(e->data, s("release")); @@ -1121,10 +1059,7 @@ void SetDebug(void *applicationPointer, int flag) { debug = flag; } -// SetMenu sets the initial menu for the application -void SetMenu(struct Application *app, const char *menuAsJSON) { - app->applicationMenu = NewApplicationMenu(menuAsJSON); -} + // SetTray sets the initial tray menu for the application void SetTray(struct Application *app, const char *trayMenuAsJSON, const char *trayLabel, const char *trayIconName) { @@ -1623,8 +1558,8 @@ struct hashmap_s *radioGroupMap) { } -// UpdateMenu replaces the current menu with the given one -void UpdateMenu(struct Application *app, const char *menuAsJSON) { +// updateMenu replaces the current menu with the given one +void updateMenu(struct Application *app, const char *menuAsJSON) { Debug(app, "Menu is now: %s", menuAsJSON); ON_MAIN_THREAD ( DeleteMenu(app->applicationMenu); @@ -1635,6 +1570,17 @@ void UpdateMenu(struct Application *app, const char *menuAsJSON) { ); } +// SetApplicationMenu sets the initial menu for the application +void SetApplicationMenu(struct Application *app, const char *menuAsJSON) { + if ( app->applicationMenu == NULL ) { + app->applicationMenu = NewApplicationMenu(menuAsJSON); + return; + } + + // Update menu + updateMenu(app, menuAsJSON); +} + //void dumpContextMenus(struct Application *app) { // dumpHashmap("menuItemMapForContextMenus", &menuItemMapForContextMenus); // printf("&menuItemMapForContextMenus = %p\n", &menuItemMapForContextMenus); @@ -2063,4 +2009,68 @@ void Run(struct Application *app, int argc, char **argv) { MEMFREE(internalCode); } + +void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) { + // Setup main application struct + struct Application *result = malloc(sizeof(struct Application)); + result->title = title; + result->width = width; + result->height = height; + result->minWidth = 0; + result->minHeight = 0; + result->maxWidth = 0; + result->maxHeight = 0; + result->resizable = resizable; + result->devtools = devtools; + result->fullscreen = fullscreen; + result->maximised = 0; + result->startHidden = startHidden; + result->decorations = 0; + result->logLevel = logLevel; + + result->mainWindow = NULL; + result->mouseEvent = NULL; + result->mouseDownMonitor = NULL; + result->mouseUpMonitor = NULL; + + // Features + result->frame = 1; + result->hideTitle = 0; + result->hideTitleBar = 0; + result->fullSizeContent = 0; + result->useToolBar = 0; + result->hideToolbarSeparator = 0; + result->appearance = NULL; + result->windowBackgroundIsTranslucent = 0; + + // Window data + result->delegate = NULL; + + // Menu + result->applicationMenu = NULL; + + // Tray + result->trayMenuAsJSON = NULL; + result->trayLabel = NULL; + result->trayIconName = NULL; + result->trayIconPosition = NSImageLeft; // Left of the text by default + result->processedTrayMenu = NULL; + result->statusItem = NULL; + + // Context Menus + result->contextMenuStore = NULL; + result->contextMenusAsJSON = NULL; + result->processedContextMenus = NULL; + contextMenuData = NULL; + + // Window Appearance + result->titlebarAppearsTransparent = 0; + result->webviewIsTranparent = 0; + + result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback; + + return (void*) result; +} + + #endif diff --git a/v2/internal/ffenestri/ffenestri_darwin.go b/v2/internal/ffenestri/ffenestri_darwin.go index 5f3e59d65..95a34ff3f 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.go +++ b/v2/internal/ffenestri/ffenestri_darwin.go @@ -4,6 +4,7 @@ package ffenestri #cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1 #cgo darwin LDFLAGS: -framework WebKit -lobjc +#include "ffenestri.h" #include "ffenestri_darwin.h" extern void TitlebarAppearsTransparent(void *); @@ -16,7 +17,6 @@ extern void DisableFrame(void *); extern void SetAppearance(void *, const char *); extern void WebviewIsTransparent(void *); extern void WindowBackgroundIsTranslucent(void *); -extern void SetMenu(void *, const char *); extern void SetTray(void *, const char *, const char *, const char *); extern void SetContextMenus(void *, const char *); @@ -79,7 +79,7 @@ func (a *Application) processPlatformSettings() error { //applicationMenu := options.GetApplicationMenu(a.config) applicationMenu := a.menuManager.GetApplicationMenuJSON() if applicationMenu != "" { - C.SetMenu(a.app, a.string2CString(applicationMenu)) + C.SetApplicationMenu(a.app, a.string2CString(applicationMenu)) } // Process tray diff --git a/v2/internal/ffenestri/menu_darwin.h b/v2/internal/ffenestri/menu_darwin.h index 645521d86..dc0863943 100644 --- a/v2/internal/ffenestri/menu_darwin.h +++ b/v2/internal/ffenestri/menu_darwin.h @@ -9,7 +9,10 @@ #include "contextmenustore_darwin.h" enum MenuItemType {Text = 0, Checkbox = 1, Radio = 2}; -enum MenuType {ApplicationMenuType = 0, ContextMenuType = 1}; +enum MenuType {ApplicationMenuType = 0, ContextMenuType = 1, TrayMenuType = 2}; +static const char *MenuTypeAsString[] = { + "ApplicationMenu", "ContextMenu", "TrayMenu", +}; extern void messageFromWindowCallback(const char *); @@ -133,14 +136,17 @@ void DeleteMenu(Menu *menu) { } // Creates a JSON message for the given menuItemID and data -const char* createMenuClickedMessage(const char *menuItemID, const char *data) { +const char* createMenuClickedMessage(const char *menuItemID, const char *data, enum MenuType menuType) { JsonNode *jsonObject = json_mkobject(); json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID)); + json_append_member(jsonObject, "menuType", json_mkstring(MenuTypeAsString[(int)menuType])); if (data != NULL) { json_append_member(jsonObject, "data", json_mkstring(data)); } - const char *result = json_encode(jsonObject); + const char *payload = json_encode(jsonObject); json_delete(jsonObject); + const char *result = concat("MC", payload); + MEMFREE(payload); return result; } @@ -177,19 +183,19 @@ void menuItemCallback(id self, SEL cmd, id sender) { msg(callbackData->menuItem, s("setState:"), NSControlStateValueOn); } + const char *menuID = callbackData->menuID; + const char *data = NULL; + enum MenuType menuType = callbackData->menu->menuType; + // Generate message to send to backend - if( callbackData->menu->menuType == ApplicationMenuType ) { - const char *clickMessage = createMenuClickedMessage(callbackData->menuID, NULL); - message = concat("MC", clickMessage); - MEMFREE(clickMessage); - } else if( callbackData->menu->menuType == ContextMenuType ) { + if( menuType == ContextMenuType ) { // Get the context menu data from the menu ContextMenuStore* store = (ContextMenuStore*) callbackData->menu->parentData; - const char *clickMessage = createMenuClickedMessage(callbackData->menuID, store->contextMenuData); - message = concat("XC", clickMessage); - MEMFREE(clickMessage); + data = store->contextMenuData; } + message = createMenuClickedMessage(menuID, data, menuType); + // TODO: Add other menu types here! // Notify the backend diff --git a/v2/internal/menumanager/menuitemmap.go b/v2/internal/menumanager/menuitemmap.go index 923e0281a..57902ea12 100644 --- a/v2/internal/menumanager/menuitemmap.go +++ b/v2/internal/menumanager/menuitemmap.go @@ -3,12 +3,17 @@ package menumanager import ( "fmt" "github.com/wailsapp/wails/v2/pkg/menu" + "sync" ) // MenuItemMap holds a mapping between menuIDs and menu items type MenuItemMap struct { idToMenuItemMap map[string]*menu.MenuItem menuItemToIDMap map[*menu.MenuItem]string + + // We use a simple counter to keep track of unique menu IDs + menuIDCounter int64 + menuIDCounterMutex sync.Mutex } func NewMenuItemMap() *MenuItemMap { @@ -37,6 +42,15 @@ func (m *MenuItemMap) Dump() { } } +// GenerateMenuID returns a unique string ID for a menu item +func (m *MenuItemMap) generateMenuID() string { + m.menuIDCounterMutex.Lock() + result := fmt.Sprintf("%d", m.menuIDCounter) + m.menuIDCounter++ + m.menuIDCounterMutex.Unlock() + return result +} + func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) { if item.SubMenu != nil { @@ -46,9 +60,13 @@ func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) { } // Create a unique ID for this menu item - menuID := fmt.Sprintf("%d", len(m.idToMenuItemMap)) + menuID := m.generateMenuID() // Store references m.idToMenuItemMap[menuID] = item m.menuItemToIDMap[item] = menuID } + +func (m *MenuItemMap) getMenuItemByID(menuId string) *menu.MenuItem { + return m.idToMenuItemMap[menuId] +} diff --git a/v2/internal/menumanager/menumanager.go b/v2/internal/menumanager/menumanager.go index ce5c2409c..9572f2c00 100644 --- a/v2/internal/menumanager/menumanager.go +++ b/v2/internal/menumanager/menumanager.go @@ -11,13 +11,13 @@ type Manager struct { applicationMenu *menu.Menu applicationMenuJSON string - // Our menu mappings - menuItemMap *MenuItemMap + // Our application menu mappings + applicationMenuItemMap *MenuItemMap } func NewManager() *Manager { return &Manager{ - menuItemMap: NewMenuItemMap(), + applicationMenuItemMap: NewMenuItemMap(), } } @@ -26,7 +26,13 @@ func (m *Manager) SetApplicationMenu(applicationMenu *menu.Menu) error { return nil } m.applicationMenu = applicationMenu - m.menuItemMap.AddMenu(applicationMenu) + + // Reset the menu map + m.applicationMenuItemMap = NewMenuItemMap() + + // Add the menu to the menu map + m.applicationMenuItemMap.AddMenu(applicationMenu) + return m.processApplicationMenu() } @@ -34,10 +40,20 @@ func (m *Manager) GetApplicationMenuJSON() string { return m.applicationMenuJSON } +// UpdateApplicationMenu reprocesses the application menu to pick up structure +// changes etc +// Returns the JSON representation of the updated menu +func (m *Manager) UpdateApplicationMenu() (string, error) { + m.applicationMenuItemMap = NewMenuItemMap() + m.applicationMenuItemMap.AddMenu(m.applicationMenu) + err := m.processApplicationMenu() + return m.applicationMenuJSON, err +} + func (m *Manager) processApplicationMenu() error { // Process the menu - processedApplicationMenu := m.NewWailsMenu(m.applicationMenu) + processedApplicationMenu := m.NewWailsMenu(m.applicationMenuItemMap, m.applicationMenu) applicationMenuJSON, err := processedApplicationMenu.AsJSON() if err != nil { return err @@ -46,14 +62,27 @@ func (m *Manager) processApplicationMenu() error { return nil } -func (m *Manager) getMenuItemByID(menuId string) *menu.MenuItem { - return m.menuItemMap.idToMenuItemMap[menuId] +func (m *Manager) getMenuItemByID(menuMap *MenuItemMap, menuId string) *menu.MenuItem { + return menuMap.idToMenuItemMap[menuId] } -func (m *Manager) ProcessClick(menuID string, data string) error { +func (m *Manager) ProcessClick(menuID string, data string, menuType string) error { + + var menuItemMap *MenuItemMap + + switch menuType { + case "ApplicationMenu": + menuItemMap = m.applicationMenuItemMap + //case "ContextMenu": + // // TBD + //case "TrayMenu": + // // TBD + default: + return fmt.Errorf("unknown menutype: %s", menuType) + } // Get the menu item - menuItem := m.getMenuItemByID(menuID) + menuItem := menuItemMap.getMenuItemByID(menuID) if menuItem == nil { return fmt.Errorf("Cannot process menuid %s - unknown", menuID) } diff --git a/v2/internal/menumanager/processedMenu.go b/v2/internal/menumanager/processedMenu.go index 293796c92..408faed13 100644 --- a/v2/internal/menumanager/processedMenu.go +++ b/v2/internal/menumanager/processedMenu.go @@ -33,9 +33,9 @@ type ProcessedMenuItem struct { Background int } -func (m *Manager) NewProcessedMenuItem(menuItem *menu.MenuItem) *ProcessedMenuItem { +func (m *Manager) NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem { - ID := m.menuItemMap.menuItemToIDMap[menuItem] + ID := menuItemMap.menuItemToIDMap[menuItem] result := &ProcessedMenuItem{ ID: ID, Label: menuItem.Label, @@ -50,7 +50,7 @@ func (m *Manager) NewProcessedMenuItem(menuItem *menu.MenuItem) *ProcessedMenuIt } if menuItem.SubMenu != nil { - result.SubMenu = m.NewProcessedMenu(menuItem.SubMenu) + result.SubMenu = m.NewProcessedMenu(menuItemMap, menuItem.SubMenu) } return result @@ -60,11 +60,11 @@ type ProcessedMenu struct { Items []*ProcessedMenuItem } -func (m *Manager) NewProcessedMenu(menu *menu.Menu) *ProcessedMenu { +func (m *Manager) NewProcessedMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *ProcessedMenu { result := &ProcessedMenu{} for _, item := range menu.Items { - processedMenuItem := m.NewProcessedMenuItem(item) + processedMenuItem := m.NewProcessedMenuItem(menuItemMap, item) result.Items = append(result.Items, processedMenuItem) } @@ -85,11 +85,11 @@ type RadioGroup struct { Length int } -func (m *Manager) NewWailsMenu(menu *menu.Menu) *WailsMenu { +func (m *Manager) NewWailsMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *WailsMenu { result := &WailsMenu{} // Process the menus - result.Menu = m.NewProcessedMenu(menu) + result.Menu = m.NewProcessedMenu(menuItemMap, menu) // Process the radio groups result.processRadioGroups() diff --git a/v2/internal/messagedispatcher/dispatchclient.go b/v2/internal/messagedispatcher/dispatchclient.go index b0d9dcb0e..e70026b71 100644 --- a/v2/internal/messagedispatcher/dispatchclient.go +++ b/v2/internal/messagedispatcher/dispatchclient.go @@ -32,7 +32,7 @@ type Client interface { WindowUnFullscreen() WindowSetColour(colour int) DarkModeEnabled(callbackID string) - UpdateMenu(menu *menu.Menu) + UpdateMenu(menuJSON string) UpdateTray(menu *menu.Menu) UpdateTrayLabel(label string) UpdateTrayIcon(name string) diff --git a/v2/internal/messagedispatcher/messagedispatcher.go b/v2/internal/messagedispatcher/messagedispatcher.go index dab03d2e5..1524acca3 100644 --- a/v2/internal/messagedispatcher/messagedispatcher.go +++ b/v2/internal/messagedispatcher/messagedispatcher.go @@ -449,11 +449,11 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) { command := splitTopic[1] switch command { - case "update": + case "updateappmenu": - updatedMenu, ok := result.Data().(*menu.Menu) + updatedMenu, ok := result.Data().(string) if !ok { - d.logger.Error("Invalid data for 'menufrontend:update' : %#v", + d.logger.Error("Invalid data for 'menufrontend:updateappmenu' : %#v", result.Data()) return } diff --git a/v2/internal/runtime/menu.go b/v2/internal/runtime/menu.go index 284e1e511..d1301b013 100644 --- a/v2/internal/runtime/menu.go +++ b/v2/internal/runtime/menu.go @@ -36,7 +36,7 @@ func (m *menuRuntime) On(menuID string, callback func(*menu.MenuItem)) { } func (m *menuRuntime) Update() { - m.bus.Publish("menu:update", m.menu) + m.bus.Publish("menu:updateappmenu", nil) } func (m *menuRuntime) GetByID(menuID string) *menu.MenuItem { diff --git a/v2/internal/subsystem/menu.go b/v2/internal/subsystem/menu.go index 6087f7666..3c48cea9c 100644 --- a/v2/internal/subsystem/menu.go +++ b/v2/internal/subsystem/menu.go @@ -99,18 +99,19 @@ func (m *Menu) Start() error { type ClickCallbackMessage struct { MenuItemID string `json:"menuItemID"` + MenuType string `json:"menuType"` Data string `json:"data"` } var callbackData ClickCallbackMessage - message := []byte(menuMessage.Data().(string)) - err := json.Unmarshal(message, &callbackData) + payload := []byte(menuMessage.Data().(string)) + err := json.Unmarshal(payload, &callbackData) if err != nil { m.logger.Error("%s", err.Error()) return } - err = m.menuManager.ProcessClick(callbackData.MenuItemID, callbackData.Data) + err = m.menuManager.ProcessClick(callbackData.MenuItemID, callbackData.Data, callbackData.MenuType) if err != nil { m.logger.Trace("%s", err.Error()) } @@ -121,12 +122,17 @@ func (m *Menu) Start() error { m.listeners[id] = append(m.listeners[id], listenerDetails.Callback) // Make sure we catch any menu updates - case "update": + case "updateappmenu": + updatedMenu, err := m.menuManager.UpdateApplicationMenu() + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } //updatedMenu := menuMessage.Data().(*menu.Menu) //m.processMenu(updatedMenu) // //// Notify frontend of menu change - //m.bus.Publish("menufrontend:update", updatedMenu) + m.bus.Publish("menufrontend:updateappmenu", updatedMenu) default: m.logger.Error("unknown menu message: %+v", menuMessage) diff --git a/v2/pkg/commands/build/base.go b/v2/pkg/commands/build/base.go index 547414f29..0eaf11000 100644 --- a/v2/pkg/commands/build/base.go +++ b/v2/pkg/commands/build/base.go @@ -148,6 +148,12 @@ func (b *BaseBuilder) CompileProject(options *Options) error { // Default go build command commands := slicer.String([]string{"build"}) + // Add better debugging flags + if options.Mode == Debug { + commands.Add("-gcflags") + commands.Add(`"all=-N -l"`) + } + // TODO: Work out if we can make this more efficient // We need to do a full build as CGO doesn't detect updates // to .h files, and we package assets into .h file. We could @@ -180,11 +186,6 @@ func (b *BaseBuilder) CompileProject(options *Options) error { } } - // Add better debugging flags - if options.Mode == Debug { - commands.Add(`-gcflags=all="-N -l"`) - } - // Get application build directory appDir := options.BuildDirectory err := cleanBuildDirectory(options) @@ -210,6 +211,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error { options.CompiledBinary = compiledBinary // Create the command + fmt.Printf("Compile command: %+v", commands.AsSlice()) cmd := exec.Command(options.Compiler, commands.AsSlice()...) // Set the directory diff --git a/v2/test/kitchensink/menu.go b/v2/test/kitchensink/menu.go index a86a26e43..b701c2df5 100644 --- a/v2/test/kitchensink/menu.go +++ b/v2/test/kitchensink/menu.go @@ -19,6 +19,9 @@ type Menu struct { lock sync.Mutex dynamicMenuItems map[string]*menu.MenuItem anotherDynamicMenuCounter int + + // Menus + removeMenuItem *menu.MenuItem } // WailsInit is called at application startup @@ -26,26 +29,6 @@ func (m *Menu) WailsInit(runtime *wails.Runtime) error { // Perform your setup here m.runtime = runtime - // Setup Menu Listeners - m.runtime.Menu.On("hello", func(mi *menu.MenuItem) { - fmt.Printf("The '%s' menu was clicked\n", mi.Label) - }) - m.runtime.Menu.On("checkbox-menu", func(mi *menu.MenuItem) { - fmt.Printf("The '%s' menu was clicked\n", mi.Label) - fmt.Printf("It is now %v\n", mi.Checked) - }) - m.runtime.Menu.On("😀option-1", func(mi *menu.MenuItem) { - fmt.Printf("We can use UTF-8 IDs: %s\n", mi.Label) - }) - - m.runtime.Menu.On("show-dynamic-menus-2", func(mi *menu.MenuItem) { - mi.Hidden = true - // Create dynamic menu items 2 submenu - m.createDynamicMenuTwo() - }) - - // Setup dynamic menus - m.runtime.Menu.On("Add Menu Item", m.addMenu) return nil } @@ -59,13 +42,14 @@ func (m *Menu) decrementcounter() int { return m.dynamicMenuCounter } -func (m *Menu) addMenu(mi *menu.MenuItem) { +func (m *Menu) addMenu(data *menu.CallbackData) { // Lock because this method will be called in a gorouting m.lock.Lock() defer m.lock.Unlock() // Get this menu's parent + mi := data.MenuItem parent := mi.Parent() counter := m.incrementcounter() menuText := "Dynamic Menu Item " + strconv.Itoa(counter) @@ -74,10 +58,8 @@ func (m *Menu) addMenu(mi *menu.MenuItem) { // If this is the first dynamic menu added, let's add a remove menu item if counter == 1 { - removeMenu := menu.Text("Remove "+menuText, - "Remove Last Item", keys.CmdOrCtrl("-"), nil) - parent.Prepend(removeMenu) - m.runtime.Menu.On("Remove Last Item", m.removeMenu) + m.removeMenuItem = menu.Text("Remove "+menuText, "Remove Last Item", keys.CmdOrCtrl("-"), m.removeMenu) + parent.Prepend(m.removeMenuItem) } else { removeMenu := m.runtime.Menu.GetByID("Remove Last Item") // Test if the remove menu hasn't already been removed in another thread @@ -88,40 +70,41 @@ func (m *Menu) addMenu(mi *menu.MenuItem) { m.runtime.Menu.Update() } -func (m *Menu) removeMenu(_ *menu.MenuItem) { - - // Lock because this method will be called in a goroutine - m.lock.Lock() - defer m.lock.Unlock() - - // Get the id of the last dynamic menu - menuID := "Dynamic Menu Item " + strconv.Itoa(m.dynamicMenuCounter) - - // Remove the last menu item by ID - m.runtime.Menu.RemoveByID(menuID) - - // Update the counter - counter := m.decrementcounter() - - // If we deleted the last dynamic menu, remove the "Remove Last Item" menu - if counter == 0 { - m.runtime.Menu.RemoveByID("Remove Last Item") - } else { - // Update label - menuText := "Dynamic Menu Item " + strconv.Itoa(counter) - removeMenu := m.runtime.Menu.GetByID("Remove Last Item") - // Test if the remove menu hasn't already been removed in another thread - if removeMenu == nil { - return - } - removeMenu.Label = "Remove " + menuText - } - - // parent.Append(menu.Text(menuText, menuText, menu.Key("["))) - m.runtime.Menu.Update() +func (m *Menu) removeMenu(_ *menu.CallbackData) { + // + //// Lock because this method will be called in a goroutine + //m.lock.Lock() + //defer m.lock.Unlock() + // + //// Remove the last menu item by ID + //m.runtime.Menu.RemoveMenuItem(menuID) + // + //// Update the counter + //counter := m.decrementcounter() + // + //// If we deleted the last dynamic menu, remove the "Remove Last Item" menu + //if counter == 0 { + // m.runtime.Menu.RemoveByID("Remove Last Item") + //} else { + // // Update label + // menuText := "Dynamic Menu Item " + strconv.Itoa(counter) + // removeMenu := m.runtime.Menu.GetByID("Remove Last Item") + // // Test if the remove menu hasn't already been removed in another thread + // if removeMenu == nil { + // return + // } + // removeMenu.Label = "Remove " + menuText + //} + // + //// parent.Append(menu.Text(menuText, menuText, menu.Key("["))) + //m.runtime.Menu.Update() } -func (m *Menu) createDynamicMenuTwo() { +func (m *Menu) createDynamicMenuTwo(data *menu.CallbackData) { + + println("\n\n\n\n\n\n\nCreating dynamic menu two\n\n\n\n\n\n") + // Hide this menu + data.MenuItem.Hidden = true // Create our submenu dm2 := menu.SubMenu("Dynamic Menus 2", menu.NewMenuFromItems( @@ -251,7 +234,7 @@ func (m *Menu) createApplicationMenu() *menu.Menu { menu.SubMenu("Test Submenu", menu.NewMenuFromItems( menu.Text("Plain text", "plain text", nil, m.processPlainText), - menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil, nil), + menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil, m.createDynamicMenuTwo), menu.SubMenu("Accelerators", menu.NewMenuFromItems( menu.SubMenu("Modifiers", menu.NewMenuFromItems( menu.Text("Shift accelerator", "Shift", keys.Shift("o"), nil), @@ -305,7 +288,7 @@ func (m *Menu) createApplicationMenu() *menu.Menu { )), )), menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", menu.NewMenuFromItems( - menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+"), nil), + menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+"), m.addMenu), menu.Separator(), )), &menu.MenuItem{ @@ -325,6 +308,10 @@ func (m *Menu) createApplicationMenu() *menu.Menu { Type: menu.CheckboxType, Accelerator: keys.CmdOrCtrl("l"), Checked: true, + Click: func(data *menu.CallbackData) { + fmt.Printf("The '%s' menu was clicked\n", data.MenuItem.Label) + fmt.Printf("It is now %v\n", data.MenuItem.Checked) + }, }, menu.Checkbox("Checkbox Menu 2", "checkbox-menu 2", false, nil, nil), menu.Separator(),