diff --git a/v2/internal/ffenestri/ffenestri.go b/v2/internal/ffenestri/ffenestri.go index 0cfe01a47..57fb1d66b 100644 --- a/v2/internal/ffenestri/ffenestri.go +++ b/v2/internal/ffenestri/ffenestri.go @@ -34,7 +34,7 @@ type Application struct { memory []unsafe.Pointer // This is the main app pointer - app unsafe.Pointer + app *C.struct_Application // Manages menus menuManager *menumanager.Manager @@ -123,7 +123,7 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel) // Save app reference - a.app = unsafe.Pointer(app) + a.app = (*C.struct_Application)(app) // Set Min Window Size minWidth := C.int(a.config.MinWidth) diff --git a/v2/internal/ffenestri/ffenestri.h b/v2/internal/ffenestri/ffenestri.h index 7613aa7b8..847ad64ff 100644 --- a/v2/internal/ffenestri/ffenestri.h +++ b/v2/internal/ffenestri/ffenestri.h @@ -2,42 +2,44 @@ #define __FFENESTRI_H__ #include +struct Application; -extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel); -extern void SetMinWindowSize(void *app, int minWidth, int minHeight); -extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight); -extern void Run(void *app, int argc, char **argv); -extern void DestroyApplication(void *app); -extern void SetDebug(void *app, int flag); -extern void SetBindings(void *app, const char *bindings); -extern void ExecJS(void *app, const char *script); -extern void Hide(void *app); -extern void Show(void *app); -extern void Center(void *app); -extern void Maximise(void *app); -extern void Unmaximise(void *app); -extern void ToggleMaximise(void *app); -extern void Minimise(void *app); -extern void Unminimise(void *app); -extern void ToggleMinimise(void *app); -extern void SetColour(void *app, int red, int green, int blue, int alpha); -extern void SetSize(void *app, int width, int height); -extern void SetPosition(void *app, int x, int y); -extern void Quit(void *app); -extern void SetTitle(void *app, const char *title); -extern void Fullscreen(void *app); -extern void UnFullscreen(void *app); -extern void ToggleFullscreen(void *app); -extern void DisableFrame(void *app); -extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories); -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 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); -extern void UpdateTrayIcon(void *app, const char *label); -extern void AddTrayMenu(void *app, const char *trayAsJSON); +extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel); +extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight); +extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight); +extern void Run(struct Application*, int argc, char **argv); +extern void DestroyApplication(struct Application*); +extern void SetDebug(struct Application*, int flag); +extern void SetBindings(struct Application*, const char *bindings); +extern void ExecJS(struct Application*, const char *script); +extern void Hide(struct Application*); +extern void Show(struct Application*); +extern void Center(struct Application*); +extern void Maximise(struct Application*); +extern void Unmaximise(struct Application*); +extern void ToggleMaximise(struct Application*); +extern void Minimise(struct Application*); +extern void Unminimise(struct Application*); +extern void ToggleMinimise(struct Application*); +extern void SetColour(struct Application*, int red, int green, int blue, int alpha); +extern void SetSize(struct Application*, int width, int height); +extern void SetPosition(struct Application*, int x, int y); +extern void Quit(struct Application*); +extern void SetTitle(struct Application*, const char *title); +extern void Fullscreen(struct Application*); +extern void UnFullscreen(struct Application*); +extern void ToggleFullscreen(struct Application*); +extern void DisableFrame(struct Application*); +extern void OpenDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories); +extern void SaveDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories); +extern void MessageDialog(struct Application*, 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(struct Application*, char *callbackID); +extern void SetApplicationMenu(struct Application*, const char *); +extern void UpdateTray(struct Application*, char *menuAsJSON); +extern void UpdateContextMenus(struct Application*, char *contextMenusAsJSON); +extern void UpdateTrayLabel(struct Application*, const char *label); +extern void UpdateTrayIcon(struct Application*, const char *label); +extern void AddTrayMenu(struct Application*, const char *trayAsJSON); +extern void UpdateTrayMenu(struct Application*, const char *trayAsJSON); #endif diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go index c1e5f4420..81a6fab38 100644 --- a/v2/internal/ffenestri/ffenestri_client.go +++ b/v2/internal/ffenestri/ffenestri_client.go @@ -206,6 +206,10 @@ func (c *Client) UpdateTray(menu *menu.Menu) { C.UpdateTray(c.app.app, c.app.string2CString(string(trayMenuJSON))) } +func (c *Client) UpdateTrayMenu(trayMenuJSON string) { + C.UpdateTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON)) +} + func (c *Client) UpdateTrayLabel(label string) { C.UpdateTrayLabel(c.app.app, c.app.string2CString(label)) } diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index d38fb4964..4aea453d2 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -925,6 +925,10 @@ void AddTrayMenu(struct Application *app, const char *trayJSON) { AddTrayMenuToStore(app->trayMenuStore, trayJSON); } +void UpdateTrayMenu(struct Application *app, const char* trayJSON) { + UpdateTrayMenuInStore(app->trayMenuStore, trayJSON); +} + void SetBindings(struct Application *app, const char *bindings) { const char* temp = concat("window.wailsbindings = \"", bindings); const char* jscall = concat(temp, "\";"); diff --git a/v2/internal/ffenestri/ffenestri_darwin.go b/v2/internal/ffenestri/ffenestri_darwin.go index c2b511982..285cbd8ea 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.go +++ b/v2/internal/ffenestri/ffenestri_darwin.go @@ -7,19 +7,7 @@ package ffenestri #include "ffenestri.h" #include "ffenestri_darwin.h" -extern void TitlebarAppearsTransparent(void *); -extern void HideTitle(void *); -extern void HideTitleBar(void *); -extern void FullSizeContent(void *); -extern void UseToolbar(void *); -extern void HideToolbarSeparator(void *); -extern void DisableFrame(void *); -extern void SetAppearance(void *, const char *); -extern void WebviewIsTransparent(void *); -extern void WindowBackgroundIsTranslucent(void *); -extern void SetTray(void *, const char *, const char *, const char *); -extern void SetContextMenus(void *, const char *); -extern void AddTrayMenu(void *, const char *); + */ import "C" diff --git a/v2/internal/ffenestri/ffenestri_darwin.h b/v2/internal/ffenestri/ffenestri_darwin.h index 34a9c8496..95dead60e 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.h +++ b/v2/internal/ffenestri/ffenestri_darwin.h @@ -94,7 +94,20 @@ #define NSAlertSecondButtonReturn 1001 #define NSAlertThirdButtonReturn 1002 +struct Application; int releaseNSObject(void *const context, struct hashmap_element_s *const e); - +void TitlebarAppearsTransparent(struct Application* app); +void HideTitle(struct Application* app); +void HideTitleBar(struct Application* app); +void FullSizeContent(struct Application* app); +void UseToolbar(struct Application* app); +void HideToolbarSeparator(struct Application* app); +void DisableFrame(struct Application* app); +void SetAppearance(struct Application* app, const char *); +void WebviewIsTransparent(struct Application* app); +void WindowBackgroundIsTranslucent(struct Application* app); +void SetTray(struct Application* app, const char *, const char *, const char *); +void SetContextMenus(struct Application* app, const char *); +void AddTrayMenu(struct Application* app, const char *); #endif \ No newline at end of file diff --git a/v2/internal/ffenestri/menu_darwin.c b/v2/internal/ffenestri/menu_darwin.c index a9b8b64a1..c687a011f 100644 --- a/v2/internal/ffenestri/menu_darwin.c +++ b/v2/internal/ffenestri/menu_darwin.c @@ -28,6 +28,10 @@ Menu* NewMenu(JsonNode *menuData) { ABORT("[NewMenu] Not enough memory to allocate radioGroupMap!"); } + // Init other members + result->menu = NULL; + result->parentData = NULL; + return result; } @@ -77,12 +81,18 @@ void DeleteMenu(Menu *menu) { hashmap_destroy(&menu->radioGroupMap); // Free up the processed menu memory - json_delete(menu->processedMenu); + if (menu->processedMenu != NULL) { + json_delete(menu->processedMenu); + menu->processedMenu = NULL; + } // Release the vector memory vec_deinit(&menu->callbackDataCache); - msg(menu->menu, s("release")); + // Free nsmenu if we have it + if ( menu->menu != NULL ) { + msg(menu->menu, s("release")); + } free(menu); } diff --git a/v2/internal/ffenestri/traymenu_darwin.c b/v2/internal/ffenestri/traymenu_darwin.c index 30a5b4160..9eb9b2620 100644 --- a/v2/internal/ffenestri/traymenu_darwin.c +++ b/v2/internal/ffenestri/traymenu_darwin.c @@ -21,6 +21,9 @@ TrayMenu* NewTrayMenu(const char* menuJSON) { ABORT("[NewTrayMenu] Unable to parse TrayMenu JSON: %s", menuJSON); } + // Save reference to this json + result->processedJSON = processedJSON; + // TODO: Make this configurable result->trayIconPosition = NSImageLeft; @@ -43,7 +46,7 @@ TrayMenu* NewTrayMenu(const char* menuJSON) { } void DumpTrayMenu(TrayMenu* trayMenu) { - printf(" ['%s':%p] = { label: '%s', icon: '%s', menu: %p }\n", trayMenu->ID, trayMenu, trayMenu->label, trayMenu->icon, trayMenu->menu ); + printf(" ['%s':%p] = { label: '%s', icon: '%s', menu: %p, statusbar: %p }\n", trayMenu->ID, trayMenu, trayMenu->label, trayMenu->icon, trayMenu->menu, trayMenu->statusbaritem ); } void ShowTrayMenu(TrayMenu* trayMenu) { @@ -53,16 +56,19 @@ void ShowTrayMenu(TrayMenu* trayMenu) { id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") ); trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength); msg(trayMenu->statusbaritem, s("retain")); - id statusBarButton = msg(trayMenu->statusbaritem, s("button")); - msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition); - // Update the icon if needed - UpdateTrayMenuIcon(trayMenu); - - // Update the label if needed - UpdateTrayMenuLabel(trayMenu); } + id statusBarButton = msg(trayMenu->statusbaritem, s("button")); + msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition); + + // Update the icon if needed + UpdateTrayMenuIcon(trayMenu); + + // Update the label if needed + UpdateTrayMenuLabel(trayMenu); + + // Update the menu id menu = GetMenu(trayMenu->menu); msg(trayMenu->statusbaritem, s("setMenu:"), menu); } @@ -80,7 +86,7 @@ void UpdateTrayMenuLabel(TrayMenu *trayMenu) { void UpdateTrayMenuIcon(TrayMenu *trayMenu) { - // Exit early if NULL or emptystring + // Exit early if NULL or empty string if( trayMenu->icon == NULL || STREMPTY(trayMenu->icon ) ) { return; } @@ -90,6 +96,30 @@ void UpdateTrayMenuIcon(TrayMenu *trayMenu) { msg(statusBarButton, s("setImage:"), trayImage); } +// UpdateTrayMenuInPlace receives 2 menus. The current menu gets +// updated with the data from the new menu. +void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) { + + // Delete the old menu + DeleteMenu(currentMenu->menu); + + // Set the new one + currentMenu->menu = newMenu->menu; + + // Delete the old JSON + json_delete(currentMenu->processedJSON); + + // Set the new JSON + currentMenu->processedJSON = newMenu->processedJSON; + + // Copy the other data + currentMenu->ID = newMenu->ID; + currentMenu->label = newMenu->label; + currentMenu->trayIconPosition = newMenu->trayIconPosition; + currentMenu->icon = newMenu->icon; + +} + void DeleteTrayMenu(TrayMenu* trayMenu) { // printf("Freeing TrayMenu:\n"); @@ -99,10 +129,17 @@ void DeleteTrayMenu(TrayMenu* trayMenu) { DeleteMenu(trayMenu->menu); // Free JSON - json_delete(trayMenu->processedJSON); + if (trayMenu->processedJSON != NULL ) { + json_delete(trayMenu->processedJSON); + } // Free the status item - msg(trayMenu->statusbaritem, s("release")); + if ( trayMenu->statusbaritem != NULL ) { + id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") ); + msg(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem); + msg(trayMenu->statusbaritem, s("release")); + trayMenu->statusbaritem = NULL; + } // Free the tray menu memory MEMFREE(trayMenu); diff --git a/v2/internal/ffenestri/traymenu_darwin.h b/v2/internal/ffenestri/traymenu_darwin.h index 5734cd103..8464a1e04 100644 --- a/v2/internal/ffenestri/traymenu_darwin.h +++ b/v2/internal/ffenestri/traymenu_darwin.h @@ -26,6 +26,7 @@ typedef struct { TrayMenu* NewTrayMenu(const char *trayJSON); void DumpTrayMenu(TrayMenu* trayMenu); void ShowTrayMenu(TrayMenu* trayMenu); +void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu); void UpdateTrayMenuIcon(TrayMenu *trayMenu); void UpdateTrayMenuLabel(TrayMenu *trayMenu); diff --git a/v2/internal/ffenestri/traymenustore_darwin.c b/v2/internal/ffenestri/traymenustore_darwin.c index 0e3a61a3c..89035d591 100644 --- a/v2/internal/ffenestri/traymenustore_darwin.c +++ b/v2/internal/ffenestri/traymenustore_darwin.c @@ -11,7 +11,7 @@ TrayMenuStore* NewTrayMenuStore() { TrayMenuStore* result = malloc(sizeof(TrayMenuStore)); - // Allocate Context Menu Store + // Allocate Tray Menu Store if( 0 != hashmap_create((const unsigned)4, &result->trayMenuMap)) { ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!"); } @@ -19,16 +19,25 @@ TrayMenuStore* NewTrayMenuStore() { return result; } +int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) { + DumpTrayMenu(e->data); + return 0; +} + +void DumpTrayMenuStore(TrayMenuStore* store) { + hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL); +} + void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) { TrayMenu* newMenu = NewTrayMenu(menuJSON); - hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); } int showTrayMenu(void *const context, struct hashmap_element_s *const e) { ShowTrayMenu(e->data); - return -1; + // 0 to retain element, -1 to delete. + return 0; } void ShowTrayMenusInStore(TrayMenuStore* store) { @@ -43,15 +52,54 @@ int freeTrayMenu(void *const context, struct hashmap_element_s *const e) { return -1; } -void DeleteTrayMenuStore(TrayMenuStore *trayMenuStore) { +void DeleteTrayMenuStore(TrayMenuStore *store) { // Delete context menus - if (hashmap_num_entries(&trayMenuStore->trayMenuMap) > 0) { - if (0 != hashmap_iterate_pairs(&trayMenuStore->trayMenuMap, freeTrayMenu, NULL)) { + if (hashmap_num_entries(&store->trayMenuMap) > 0) { + if (0 != hashmap_iterate_pairs(&store->trayMenuMap, freeTrayMenu, NULL)) { ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!"); } } // Destroy tray menu map - hashmap_destroy(&trayMenuStore->trayMenuMap); + hashmap_destroy(&store->trayMenuMap); +} + +TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) { + // Get the current menu + return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID)); +} + +void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) { + TrayMenu* newMenu = NewTrayMenu(menuJSON); + + // Get the current menu + TrayMenu *currentMenu = GetTrayMenuFromStore(store, newMenu->ID); + if ( currentMenu == NULL ) { + ABORT("Attempted to update unknown tray menu with ID '%s'.", newMenu->ID); + } + + // Save the status bar reference + newMenu->statusbaritem = currentMenu->statusbaritem; + + hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID)); + + // Delete the current menu + DeleteMenu(currentMenu->menu); + currentMenu->menu = NULL; + + // Free JSON + if (currentMenu->processedJSON != NULL ) { + json_delete(currentMenu->processedJSON); + currentMenu->processedJSON = NULL; + } + + // Free the tray menu memory + MEMFREE(currentMenu); + + hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); + + // Show the updated menu + ShowTrayMenu(newMenu); + } diff --git a/v2/internal/ffenestri/traymenustore_darwin.h b/v2/internal/ffenestri/traymenustore_darwin.h index 3f77c519c..704cb60a3 100644 --- a/v2/internal/ffenestri/traymenustore_darwin.h +++ b/v2/internal/ffenestri/traymenustore_darwin.h @@ -18,6 +18,7 @@ typedef struct { TrayMenuStore* NewTrayMenuStore(); void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON); +void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON); void ShowTrayMenusInStore(TrayMenuStore* store); void DeleteTrayMenuStore(TrayMenuStore* store); diff --git a/v2/internal/menumanager/menumanager.go b/v2/internal/menumanager/menumanager.go index d1cfc515b..092ef5e9e 100644 --- a/v2/internal/menumanager/menumanager.go +++ b/v2/internal/menumanager/menumanager.go @@ -17,8 +17,9 @@ type Manager struct { // Context menus contextMenus map[string]*ContextMenu - // Tray menus - trayMenus map[string]*TrayMenu + // Tray menu stores + trayMenus map[string]*TrayMenu + trayMenuPointers map[*menu.TrayMenu]string } func NewManager() *Manager { @@ -26,6 +27,7 @@ func NewManager() *Manager { applicationMenuItemMap: NewMenuItemMap(), contextMenus: make(map[string]*ContextMenu), trayMenus: make(map[string]*TrayMenu), + trayMenuPointers: make(map[*menu.TrayMenu]string), } } diff --git a/v2/internal/menumanager/traymenu.go b/v2/internal/menumanager/traymenu.go index d62d3652a..1ad53ef79 100644 --- a/v2/internal/menumanager/traymenu.go +++ b/v2/internal/menumanager/traymenu.go @@ -27,10 +27,17 @@ type TrayMenu struct { ProcessedMenu *WailsMenu } +func (t *TrayMenu) AsJSON() (string, error) { + data, err := json.Marshal(t) + if err != nil { + return "", err + } + return string(data), nil +} + func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu { result := &TrayMenu{ - ID: generateTrayID(), Label: trayMenu.Label, Icon: trayMenu.Icon, menu: trayMenu.Menu, @@ -45,17 +52,40 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu { func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) { newTrayMenu := NewTrayMenu(trayMenu) - m.trayMenus[newTrayMenu.ID] = newTrayMenu + + // Hook up a new ID + trayID := generateTrayID() + newTrayMenu.ID = trayID + + // Save the references + m.trayMenus[trayID] = newTrayMenu + m.trayMenuPointers[trayMenu] = trayID +} + +func (m *Manager) UpdateTrayMenu(trayMenu *menu.TrayMenu) (string, error) { + trayID, trayMenuKnown := m.trayMenuPointers[trayMenu] + if !trayMenuKnown { + return "", fmt.Errorf("unknown Tray Menu '%s'. Please add the tray menu using AddTrayMenu()", trayMenu.Label) + } + + // Create the updated tray menu + updatedTrayMenu := NewTrayMenu(trayMenu) + updatedTrayMenu.ID = trayID + + // Save the reference + m.trayMenus[trayID] = updatedTrayMenu + + return updatedTrayMenu.AsJSON() } func (m *Manager) GetTrayMenus() ([]string, error) { result := []string{} for _, trayMenu := range m.trayMenus { - data, err := json.Marshal(trayMenu) + JSON, err := trayMenu.AsJSON() if err != nil { return nil, err } - result = append(result, string(data)) + result = append(result, JSON) } return result, nil diff --git a/v2/internal/messagedispatcher/dispatchclient.go b/v2/internal/messagedispatcher/dispatchclient.go index e70026b71..a44aa06b6 100644 --- a/v2/internal/messagedispatcher/dispatchclient.go +++ b/v2/internal/messagedispatcher/dispatchclient.go @@ -36,6 +36,7 @@ type Client interface { UpdateTray(menu *menu.Menu) UpdateTrayLabel(label string) UpdateTrayIcon(name string) + UpdateTrayMenu(menuJSON string) UpdateContextMenus(contextMenus *menu.ContextMenus) } diff --git a/v2/internal/messagedispatcher/messagedispatcher.go b/v2/internal/messagedispatcher/messagedispatcher.go index 1524acca3..630947c4b 100644 --- a/v2/internal/messagedispatcher/messagedispatcher.go +++ b/v2/internal/messagedispatcher/messagedispatcher.go @@ -464,6 +464,20 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) { client.frontend.UpdateMenu(updatedMenu) } + case "updatetraymenu": + updatedTrayMenu, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updatetraymenu' : %#v", + result.Data()) + return + } + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.UpdateTrayMenu(updatedTrayMenu) + } + default: d.logger.Error("Unknown menufrontend command: %s", command) } diff --git a/v2/internal/runtime/menu.go b/v2/internal/runtime/menu.go index a6ac896ed..34fe75f6d 100644 --- a/v2/internal/runtime/menu.go +++ b/v2/internal/runtime/menu.go @@ -9,6 +9,7 @@ import ( type Menu interface { UpdateApplicationMenu() UpdateContextMenu(contextMenu *menu.ContextMenu) + UpdateTrayMenu(trayMenu *menu.TrayMenu) } type menuRuntime struct { @@ -29,3 +30,7 @@ func (m *menuRuntime) UpdateApplicationMenu() { func (m *menuRuntime) UpdateContextMenu(contextMenu *menu.ContextMenu) { m.bus.Publish("menu:updatecontextmenu", contextMenu) } + +func (m *menuRuntime) UpdateTrayMenu(trayMenu *menu.TrayMenu) { + m.bus.Publish("menu:updatetraymenu", trayMenu) +} diff --git a/v2/internal/subsystem/menu.go b/v2/internal/subsystem/menu.go index c9d234225..80bdf3732 100644 --- a/v2/internal/subsystem/menu.go +++ b/v2/internal/subsystem/menu.go @@ -2,6 +2,7 @@ package subsystem import ( "encoding/json" + "github.com/wailsapp/wails/v2/pkg/menu" "strings" "github.com/wailsapp/wails/v2/internal/logger" @@ -119,6 +120,16 @@ func (m *Menu) Start() error { // //// Notify frontend of menu change //m.bus.Publish("menufrontend:updatecontextmenu", updatedMenu) + case "updatetraymenu": + trayMenu := menuMessage.Data().(*menu.TrayMenu) + updatedMenu, err := m.menuManager.UpdateTrayMenu(trayMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:updatetraymenu", updatedMenu) default: m.logger.Error("unknown menu message: %+v", menuMessage) diff --git a/v2/test/kitchensink/tray.go b/v2/test/kitchensink/tray.go index d6e60c966..2a7361399 100644 --- a/v2/test/kitchensink/tray.go +++ b/v2/test/kitchensink/tray.go @@ -3,16 +3,17 @@ package main import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/menu" + "strconv" + "sync" ) // Tray struct type Tray struct { runtime *wails.Runtime - // - //dynamicMenuCounter int - //lock sync.Mutex - //dynamicMenuItems map[string]*menu.MenuItem - //anotherDynamicMenuCounter int + + dynamicMenuCounter int + lock sync.Mutex + dynamicMenuItems map[string]*menu.MenuItem trayMenu *menu.TrayMenu secondTrayMenu *menu.TrayMenu @@ -24,23 +25,7 @@ type Tray struct { func (t *Tray) WailsInit(runtime *wails.Runtime) error { // Perform your setup here t.runtime = runtime - // - //// Setup Menu Listeners - //t.runtime.Tray.On("Show Window", func(mi *menu.MenuItem) { - // t.runtime.Window.Show() - //}) - //t.runtime.Tray.On("Hide Window", func(mi *menu.MenuItem) { - // t.runtime.Window.Hide() - //}) - // - //t.runtime.Tray.On("Minimise Window", func(mi *menu.MenuItem) { - // t.runtime.Window.Minimise() - //}) - // - //t.runtime.Tray.On("Unminimise Window", func(mi *menu.MenuItem) { - // t.runtime.Window.Unminimise() - //}) - // + //// Auto switch between light / dark tray icons //t.runtime.Events.OnThemeChange(func(darkMode bool) { // if darkMode { @@ -74,45 +59,16 @@ func (t *Tray) WailsShutdown() { t.done = true } -//func (t *Tray) incrementcounter() int { -// t.dynamicMenuCounter++ -// return t.dynamicMenuCounter -//} -// -//func (t *Tray) decrementcounter() int { -// t.dynamicMenuCounter-- -// return t.dynamicMenuCounter -//} -// -//func (t *Tray) addMenu(mi *menu.MenuItem) { -// -// // Lock because this method will be called in a gorouting -// t.lock.Lock() -// defer t.lock.Unlock() -// -// // Get this menu's parent -// parent := mi.Parent() -// counter := t.incrementcounter() -// menuText := "Dynamic Menu Item " + strconv.Itoa(counter) -// parent.Append(menu.Text(menuText, menuText, nil, nil)) -// // parent.Append(menu.Text(menuText, menuText, menu.Key("["))) -// -// // 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) -// t.runtime.Tray.On("Remove Last Item", t.removeMenu) -// } else { -// removeMenu := t.runtime.Tray.GetByID("Remove Last Item") -// // Test if the remove menu hasn't already been removed in another thread -// if removeMenu != nil { -// removeMenu.Label = "Remove " + menuText -// } -// } -// t.runtime.Tray.Update() -//} -// +func (t *Tray) incrementcounter() int { + t.dynamicMenuCounter++ + return t.dynamicMenuCounter +} + +func (t *Tray) decrementcounter() int { + t.dynamicMenuCounter-- + return t.dynamicMenuCounter +} + //func (t *Tray) removeMenu(_ *menu.MenuItem) { // // // Lock because this method will be called in a goroutine @@ -164,10 +120,16 @@ func (t *Tray) createTrayMenus() []*menu.TrayMenu { secondTrayMenu := &menu.TrayMenu{} secondTrayMenu.Label = "Another tray label" secondTrayMenu.Menu = menu.NewMenuFromItems( - menu.Text("Show Window", "Show Window", nil, t.showWindow), - menu.Text("Hide Window", "Hide Window", nil, t.hideWindow), - menu.Text("Minimise Window", "Minimise Window", nil, t.minimiseWindow), - menu.Text("Unminimise Window", "Unminimise Window", nil, t.unminimiseWindow), + menu.Text("Update Label", "Update Label", nil, func(_ *menu.CallbackData) { + // Lock because this method will be called in a goroutine + t.lock.Lock() + defer t.lock.Unlock() + + counter := t.incrementcounter() + trayLabel := "Updated Label " + strconv.Itoa(counter) + secondTrayMenu.Label = trayLabel + t.runtime.Menu.UpdateTrayMenu(t.secondTrayMenu) + }), ) t.secondTrayMenu = secondTrayMenu return []*menu.TrayMenu{