From 47bca0be888409f572214d05486919993914e213 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 16 Jan 2021 11:35:49 +1100 Subject: [PATCH] Support updating tray labels in an efficient manner --- v2/internal/ffenestri/common.c | 7 +++ v2/internal/ffenestri/common.h | 2 + v2/internal/ffenestri/ffenestri.h | 1 + v2/internal/ffenestri/ffenestri_client.go | 4 ++ v2/internal/ffenestri/ffenestri_darwin.c | 10 +++- v2/internal/ffenestri/ffenestri_darwin.h | 2 +- v2/internal/ffenestri/menu_darwin.c | 4 ++ v2/internal/ffenestri/traymenu_darwin.c | 57 ++++++++++--------- v2/internal/ffenestri/traymenu_darwin.h | 4 +- v2/internal/ffenestri/traymenustore_darwin.c | 33 ++++++++--- v2/internal/ffenestri/traymenustore_darwin.h | 2 + v2/internal/menumanager/traymenu.go | 26 +++++++++ .../messagedispatcher/dispatchclient.go | 1 + .../messagedispatcher/messagedispatcher.go | 14 +++++ v2/internal/runtime/menu.go | 5 ++ v2/internal/subsystem/menu.go | 11 ++++ v2/pkg/str/str.go | 10 ++++ 17 files changed, 155 insertions(+), 38 deletions(-) create mode 100644 v2/pkg/str/str.go diff --git a/v2/internal/ffenestri/common.c b/v2/internal/ffenestri/common.c index dcd46083..fcec3564 100644 --- a/v2/internal/ffenestri/common.c +++ b/v2/internal/ffenestri/common.c @@ -86,3 +86,10 @@ bool getJSONInt(JsonNode *item, const char* key, int *result) { return false; } +JsonNode* mustParseJSON(const char* JSON) { + JsonNode* parsedUpdate = json_decode(JSON); + if ( parsedUpdate == NULL ) { + ABORT("Unable to decode JSON: %s\n", JSON); + } + return parsedUpdate; +} \ No newline at end of file diff --git a/v2/internal/ffenestri/common.h b/v2/internal/ffenestri/common.h index b289555a..c0256935 100644 --- a/v2/internal/ffenestri/common.h +++ b/v2/internal/ffenestri/common.h @@ -35,4 +35,6 @@ JsonNode* mustJSONObject(JsonNode *node, const char* key); bool getJSONBool(JsonNode *item, const char* key, bool *result); bool getJSONInt(JsonNode *item, const char* key, int *result); +JsonNode* mustParseJSON(const char* JSON); + #endif //ASSETS_C_COMMON_H diff --git a/v2/internal/ffenestri/ffenestri.h b/v2/internal/ffenestri/ffenestri.h index 532f2bd9..5f630a7d 100644 --- a/v2/internal/ffenestri/ffenestri.h +++ b/v2/internal/ffenestri/ffenestri.h @@ -37,6 +37,7 @@ extern void DarkModeEnabled(struct Application*, char *callbackID); extern void SetApplicationMenu(struct Application*, const char *); extern void AddTrayMenu(struct Application*, const char *menuTrayJSON); extern void SetTrayMenu(struct Application*, const char *menuTrayJSON); +extern void UpdateTrayMenuLabel(struct Application*, const char* JSON); extern void AddContextMenu(struct Application*, char *contextMenuJSON); extern void UpdateContextMenu(struct Application*, char *contextMenuJSON); diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go index 23285d74..b0e7607a 100644 --- a/v2/internal/ffenestri/ffenestri_client.go +++ b/v2/internal/ffenestri/ffenestri_client.go @@ -192,6 +192,10 @@ func (c *Client) SetTrayMenu(trayMenuJSON string) { C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON)) } +func (c *Client) UpdateTrayMenuLabel(JSON string) { + C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON)) +} + func (c *Client) UpdateContextMenu(contextMenuJSON string) { C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON)) } diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index a784f5b1..e59fba4b 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -913,7 +913,8 @@ void SetDebug(void *applicationPointer, int flag) { } -// SetContextMenus sets the context menu map for this application + +// AddContextMenu sets the context menu map for this application void AddContextMenu(struct Application *app, const char *contextMenuJSON) { AddContextMenuToStore(app->contextMenuStore, contextMenuJSON); } @@ -932,6 +933,13 @@ void SetTrayMenu(struct Application *app, const char* trayMenuJSON) { ); } +void UpdateTrayMenuLabel(struct Application* app, const char* JSON) { + ON_MAIN_THREAD( + UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON); + ); +} + + 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.h b/v2/internal/ffenestri/ffenestri_darwin.h index 95dead60..ddb94f8a 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.h +++ b/v2/internal/ffenestri/ffenestri_darwin.h @@ -107,7 +107,7 @@ 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 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 c687a011..ba583b40 100644 --- a/v2/internal/ffenestri/menu_darwin.c +++ b/v2/internal/ffenestri/menu_darwin.c @@ -99,7 +99,11 @@ void DeleteMenu(Menu *menu) { // Creates a JSON message for the given menuItemID and data const char* createMenuClickedMessage(const char *menuItemID, const char *data, enum MenuType menuType, const char *parentID) { + JsonNode *jsonObject = json_mkobject(); + if (menuItemID == NULL ) { + ABORT("Item ID NULL for menu!!\n"); + } json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID)); json_append_member(jsonObject, "menuType", json_mkstring(MenuTypeAsString[(int)menuType])); if (data != NULL) { diff --git a/v2/internal/ffenestri/traymenu_darwin.c b/v2/internal/ffenestri/traymenu_darwin.c index c2afaffc..5919a615 100644 --- a/v2/internal/ffenestri/traymenu_darwin.c +++ b/v2/internal/ffenestri/traymenu_darwin.c @@ -49,42 +49,19 @@ void DumpTrayMenu(TrayMenu* trayMenu) { 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) { - // Create a status bar item if we don't have one - if( trayMenu->statusbaritem == NULL ) { - 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); - - // Update the menu - id menu = GetMenu(trayMenu->menu); - msg(trayMenu->statusbaritem, s("setMenu:"), menu); -} - -void UpdateTrayMenuLabel(TrayMenu *trayMenu) { +void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) { // Exit early if NULL if( trayMenu->label == NULL ) { return; } - // We don't check for a + // Update button label id statusBarButton = msg(trayMenu->statusbaritem, s("button")); - msg(statusBarButton, s("setTitle:"), str(trayMenu->label)); + msg(statusBarButton, s("setTitle:"), str(label)); } -void UpdateTrayMenuIcon(TrayMenu *trayMenu) { +void UpdateTrayIcon(TrayMenu *trayMenu) { // Exit early if NULL if( trayMenu->icon == NULL ) { @@ -105,6 +82,32 @@ void UpdateTrayMenuIcon(TrayMenu *trayMenu) { msg(statusBarButton, s("setImage:"), trayImage); } + + +void ShowTrayMenu(TrayMenu* trayMenu) { + + // Create a status bar item if we don't have one + if( trayMenu->statusbaritem == NULL ) { + 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 + UpdateTrayIcon(trayMenu); + + // Update the label if needed + UpdateTrayLabel(trayMenu, trayMenu->label); + + // Update the menu + id menu = GetMenu(trayMenu->menu); + msg(trayMenu->statusbaritem, s("setMenu:"), menu); +} + // UpdateTrayMenuInPlace receives 2 menus. The current menu gets // updated with the data from the new menu. void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) { diff --git a/v2/internal/ffenestri/traymenu_darwin.h b/v2/internal/ffenestri/traymenu_darwin.h index 8464a1e0..6b69f4b1 100644 --- a/v2/internal/ffenestri/traymenu_darwin.h +++ b/v2/internal/ffenestri/traymenu_darwin.h @@ -27,8 +27,8 @@ 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); +void UpdateTrayIcon(TrayMenu *trayMenu); +void UpdateTrayLabel(TrayMenu *trayMenu, const char*); void LoadTrayIcons(); void UnloadTrayIcons(); diff --git a/v2/internal/ffenestri/traymenustore_darwin.c b/v2/internal/ffenestri/traymenustore_darwin.c index 4fd89f07..6c451a47 100644 --- a/v2/internal/ffenestri/traymenustore_darwin.c +++ b/v2/internal/ffenestri/traymenustore_darwin.c @@ -72,15 +72,39 @@ TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) { return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID)); } +TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) { + // Get the current menu + TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID)); + if (result == NULL ) { + ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID); + } + return result; +} + +void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) { + // Parse the JSON + JsonNode *parsedUpdate = mustParseJSON(JSON); + + // Get the data out + const char* ID = mustJSONString(parsedUpdate, "ID"); + const char* Label = mustJSONString(parsedUpdate, "Label"); + + // Check we have this menu + TrayMenu *menu = MustGetTrayMenuFromStore(store, ID); + UpdateTrayLabel(menu, Label); + +} + void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) { TrayMenu* newMenu = NewTrayMenu(menuJSON); + DumpTrayMenu(newMenu); // Get the current menu TrayMenu *currentMenu = GetTrayMenuFromStore(store, newMenu->ID); // If we don't have a menu, we create one if ( currentMenu == NULL ) { - + printf(" currentMenu = NULL\n"); // Store the new menu hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); @@ -88,6 +112,7 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) { ShowTrayMenu(newMenu); return; } + DumpTrayMenu(currentMenu); // Save the status bar reference newMenu->statusbaritem = currentMenu->statusbaritem; @@ -98,12 +123,6 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) { 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); diff --git a/v2/internal/ffenestri/traymenustore_darwin.h b/v2/internal/ffenestri/traymenustore_darwin.h index 704cb60a..99040f47 100644 --- a/v2/internal/ffenestri/traymenustore_darwin.h +++ b/v2/internal/ffenestri/traymenustore_darwin.h @@ -22,4 +22,6 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON); void ShowTrayMenusInStore(TrayMenuStore* store); void DeleteTrayMenuStore(TrayMenuStore* store); +void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON); + #endif //TRAYMENUSTORE_DARWIN_H diff --git a/v2/internal/menumanager/traymenu.go b/v2/internal/menumanager/traymenu.go index 2771a299..8a4df366 100644 --- a/v2/internal/menumanager/traymenu.go +++ b/v2/internal/menumanager/traymenu.go @@ -3,6 +3,7 @@ package menumanager import ( "encoding/json" "fmt" + "github.com/pkg/errors" "github.com/wailsapp/wails/v2/pkg/menu" "sync" ) @@ -94,6 +95,31 @@ func (m *Manager) GetTrayMenus() ([]string, error) { return result, nil } +func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) { + trayID, trayMenuKnown := m.trayMenuPointers[trayMenu] + if !trayMenuKnown { + return "", fmt.Errorf("[UpdateTrayMenuLabel] unknown tray id for tray %s", trayMenu.Label) + } + + type LabelUpdate struct { + ID string + Label string + } + + update := &LabelUpdate{ + ID: trayID, + Label: trayMenu.Label, + } + + data, err := json.Marshal(update) + if err != nil { + return "", errors.Wrap(err, "[UpdateTrayMenuLabel] ") + } + + return string(data), nil + +} + func (m *Manager) GetContextMenus() ([]string, error) { result := []string{} for _, contextMenu := range m.contextMenus { diff --git a/v2/internal/messagedispatcher/dispatchclient.go b/v2/internal/messagedispatcher/dispatchclient.go index 5037b971..b2084113 100644 --- a/v2/internal/messagedispatcher/dispatchclient.go +++ b/v2/internal/messagedispatcher/dispatchclient.go @@ -32,6 +32,7 @@ type Client interface { DarkModeEnabled(callbackID string) SetApplicationMenu(menuJSON string) SetTrayMenu(trayMenuJSON string) + UpdateTrayMenuLabel(JSON string) UpdateContextMenu(contextMenuJSON string) } diff --git a/v2/internal/messagedispatcher/messagedispatcher.go b/v2/internal/messagedispatcher/messagedispatcher.go index 4d41bcb7..3001a293 100644 --- a/v2/internal/messagedispatcher/messagedispatcher.go +++ b/v2/internal/messagedispatcher/messagedispatcher.go @@ -473,6 +473,20 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) { client.frontend.UpdateContextMenu(updatedContextMenu) } + case "updatetraymenulabel": + updatedTrayMenuLabel, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#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.UpdateTrayMenuLabel(updatedTrayMenuLabel) + } + default: d.logger.Error("Unknown menufrontend command: %s", command) } diff --git a/v2/internal/runtime/menu.go b/v2/internal/runtime/menu.go index 710f2eaa..283b639b 100644 --- a/v2/internal/runtime/menu.go +++ b/v2/internal/runtime/menu.go @@ -10,6 +10,7 @@ type Menu interface { UpdateApplicationMenu() UpdateContextMenu(contextMenu *menu.ContextMenu) SetTrayMenu(trayMenu *menu.TrayMenu) + UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) } type menuRuntime struct { @@ -34,3 +35,7 @@ func (m *menuRuntime) UpdateContextMenu(contextMenu *menu.ContextMenu) { func (m *menuRuntime) SetTrayMenu(trayMenu *menu.TrayMenu) { m.bus.Publish("menu:settraymenu", trayMenu) } + +func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) { + m.bus.Publish("menu:updatetraymenulabel", trayMenu) +} diff --git a/v2/internal/subsystem/menu.go b/v2/internal/subsystem/menu.go index 91e63f92..81ff1033 100644 --- a/v2/internal/subsystem/menu.go +++ b/v2/internal/subsystem/menu.go @@ -131,6 +131,17 @@ func (m *Menu) Start() error { // Notify frontend of menu change m.bus.Publish("menufrontend:settraymenu", updatedMenu) + case "updatetraymenulabel": + trayMenu := menuMessage.Data().(*menu.TrayMenu) + updatedLabel, err := m.menuManager.UpdateTrayMenuLabel(trayMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:updatetraymenulabel", updatedLabel) + default: m.logger.Error("unknown menu message: %+v", menuMessage) } diff --git a/v2/pkg/str/str.go b/v2/pkg/str/str.go new file mode 100644 index 00000000..d27d5dad --- /dev/null +++ b/v2/pkg/str/str.go @@ -0,0 +1,10 @@ +package str + +import ( + "fmt" + "time" +) + +func UnixNow() string { + return fmt.Sprintf("%+v", time.Now().Unix()) +}