mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 05:08:54 -07:00
Initial support for multiple traymenus
This commit is contained in:
@@ -45,6 +45,29 @@ const char* getJSONString(JsonNode *item, const char* key) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void ABORT_JSON(JsonNode *node, const char* key) {
|
||||
ABORT("Unable to read required key '%s' from JSON: %s\n", key, json_encode(node));
|
||||
}
|
||||
|
||||
const char* mustJSONString(JsonNode *node, const char* key) {
|
||||
const char* result = getJSONString(node, key);
|
||||
if ( result == NULL ) {
|
||||
ABORT_JSON(node, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
JsonNode* mustJSONObject(JsonNode *node, const char* key) {
|
||||
struct JsonNode* result = getJSONObject(node, key);
|
||||
if ( result == NULL ) {
|
||||
ABORT_JSON(node, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JsonNode* getJSONObject(JsonNode* node, const char* key) {
|
||||
return json_find_member(node, key);
|
||||
}
|
||||
|
||||
bool getJSONBool(JsonNode *item, const char* key, bool *result) {
|
||||
JsonNode *node = json_find_member(item, key);
|
||||
if ( node != NULL && node->tag == JSON_BOOL) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "json.h"
|
||||
|
||||
#define STREQ(a,b) strcmp(a, b) == 0
|
||||
#define STREMPTY(string) strlen(string) == 0
|
||||
#define STRCOPY(a) concat(a, "")
|
||||
#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0
|
||||
#define MEMFREE(input) free((void*)input); input = NULL;
|
||||
@@ -27,6 +28,10 @@ char* concat(const char *string1, const char *string2);
|
||||
void ABORT(const char *message, ...);
|
||||
int freeHashmapItem(void *const context, struct hashmap_element_s *const e);
|
||||
const char* getJSONString(JsonNode *item, const char* key);
|
||||
const char* mustJSONString(JsonNode *node, const char* key);
|
||||
JsonNode* getJSONObject(JsonNode* node, const char* key);
|
||||
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);
|
||||
|
||||
|
||||
@@ -1999,6 +1999,9 @@ void Run(struct Application *app, int argc, char **argv) {
|
||||
msg(msg(c("NSApplication"), s("sharedApplication")), s("setMainMenu:"), menu);
|
||||
}
|
||||
|
||||
// Setup initial trays
|
||||
ShowTrayMenusInStore(app->trayMenuStore);
|
||||
|
||||
// If we have a tray menu, process it
|
||||
if( app->trayMenuAsJSON != NULL ) {
|
||||
parseTrayData(app);
|
||||
|
||||
@@ -149,12 +149,12 @@ void menuItemCallback(id self, SEL cmd, id sender) {
|
||||
ContextMenu* contextMenu = (ContextMenu*) callbackData->menu->parentData;
|
||||
data = contextMenu->contextMenuData;
|
||||
parentID = contextMenu->ID;
|
||||
} else if ( menuType == TrayMenuType ) {
|
||||
parentID = (const char*) callbackData->menu->parentData;
|
||||
}
|
||||
|
||||
message = createMenuClickedMessage(menuID, data, menuType, parentID);
|
||||
|
||||
// TODO: Add other menu types here!
|
||||
|
||||
// Notify the backend
|
||||
messageFromWindowCallback(message);
|
||||
MEMFREE(message);
|
||||
|
||||
@@ -5,17 +5,96 @@
|
||||
#include "common.h"
|
||||
#include "traymenu_darwin.h"
|
||||
|
||||
extern struct hashmap_s trayIconCache;
|
||||
|
||||
TrayMenu* NewTrayMenu(const char* menuJSON) {
|
||||
TrayMenu* result = malloc(sizeof(TrayMenu));
|
||||
|
||||
/*
|
||||
{"ID":"0","Label":"Test Tray Label","Icon":"","ProcessedMenu":{"Menu":{"Items":[{"ID":"0","Label":"Show Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"1","Label":"Hide Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"2","Label":"Minimise Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"3","Label":"Unminimise Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0}]},"RadioGroups":null}}
|
||||
*/
|
||||
JsonNode* processedJSON = json_decode(menuJSON);
|
||||
if( processedJSON == NULL ) {
|
||||
ABORT("[NewTrayMenu] Unable to parse TrayMenu JSON: %s", menuJSON);
|
||||
}
|
||||
|
||||
// TODO: Make this configurable
|
||||
result->trayIconPosition = NSImageLeft;
|
||||
|
||||
result->ID = mustJSONString(processedJSON, "ID");
|
||||
result->label = mustJSONString(processedJSON, "Label");
|
||||
result->icon = mustJSONString(processedJSON, "Icon");
|
||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||
|
||||
// Create the menu
|
||||
result->menu = NewMenu(processedMenu);
|
||||
|
||||
// Set the menu type and store the tray ID in the parent data
|
||||
result->menu->menuType = TrayMenuType;
|
||||
result->menu->parentData = (void*) result->ID;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DumpTrayMenu(TrayMenu* trayMenu) {
|
||||
printf(" ['%s':%p] = { label: '%s', icon: '%s', menu: %p }\n", trayMenu->ID, trayMenu, trayMenu->label, trayMenu->icon, trayMenu->menu );
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
id menu = GetMenu(trayMenu->menu);
|
||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||
}
|
||||
|
||||
void UpdateTrayMenuLabel(TrayMenu *trayMenu) {
|
||||
|
||||
// Exit early if NULL
|
||||
if( trayMenu->label == NULL ) {
|
||||
return;
|
||||
}
|
||||
// We don't check for a
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setTitle:"), str(trayMenu->label));
|
||||
}
|
||||
|
||||
void UpdateTrayMenuIcon(TrayMenu *trayMenu) {
|
||||
|
||||
// Exit early if NULL or emptystring
|
||||
if( trayMenu->icon == NULL || STREMPTY(trayMenu->icon ) ) {
|
||||
return;
|
||||
}
|
||||
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
msg(statusBarButton, s("setImage:"), trayImage);
|
||||
}
|
||||
|
||||
void DeleteTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// printf("Freeing TrayMenu:\n");
|
||||
// DumpTrayMenu(trayMenu);
|
||||
|
||||
// Delete the menu
|
||||
DeleteMenu(trayMenu->menu);
|
||||
|
||||
// Free JSON
|
||||
json_delete(trayMenu->processedJSON);
|
||||
|
||||
// Free the tray menu memory
|
||||
MEMFREE(trayMenu);
|
||||
}
|
||||
@@ -5,19 +5,30 @@
|
||||
#ifndef TRAYMENU_DARWIN_H
|
||||
#define TRAYMENU_DARWIN_H
|
||||
|
||||
#include "common.h"
|
||||
#include "menu_darwin.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
const char *label;
|
||||
const char *icon;
|
||||
const char *trayID;
|
||||
const char *ID;
|
||||
|
||||
Menu* menu;
|
||||
|
||||
id statusbaritem;
|
||||
int trayIconPosition;
|
||||
|
||||
JsonNode* processedJSON;
|
||||
|
||||
} TrayMenu;
|
||||
|
||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||
void DumpTrayMenu(TrayMenu* trayMenu);
|
||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||
void UpdateTrayMenuIcon(TrayMenu *trayMenu);
|
||||
void UpdateTrayMenuLabel(TrayMenu *trayMenu);
|
||||
|
||||
void DeleteTrayMenu(TrayMenu* trayMenu);
|
||||
|
||||
#endif //TRAYMENU_DARWIN_H
|
||||
|
||||
@@ -21,12 +21,22 @@ TrayMenuStore* NewTrayMenuStore() {
|
||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||
|
||||
const char *ID = "TEST";
|
||||
|
||||
hashmap_put(&store->trayMenuMap, ID, strlen(ID), newMenu);
|
||||
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;
|
||||
}
|
||||
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
||||
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
||||
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
DeleteTrayMenu(e->data);
|
||||
return -1;
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef struct {
|
||||
TrayMenuStore* NewTrayMenuStore();
|
||||
|
||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON);
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||
|
||||
#endif //TRAYMENUSTORE_DARWIN_H
|
||||
|
||||
@@ -41,14 +41,17 @@ func (m *Manager) ProcessClick(menuID string, data string, menuType string, pare
|
||||
case "ApplicationMenu":
|
||||
menuItemMap = m.applicationMenuItemMap
|
||||
case "ContextMenu":
|
||||
// TBD
|
||||
contextMenu := m.contextMenus[parentID]
|
||||
if contextMenu == nil {
|
||||
return fmt.Errorf("unknown context menu: %s", parentID)
|
||||
}
|
||||
menuItemMap = contextMenu.menuItemMap
|
||||
//case "TrayMenu":
|
||||
// // TBD
|
||||
case "TrayMenu":
|
||||
trayMenu := m.trayMenus[parentID]
|
||||
if trayMenu == nil {
|
||||
return fmt.Errorf("unknown tray menu: %s", parentID)
|
||||
}
|
||||
menuItemMap = trayMenu.menuItemMap
|
||||
default:
|
||||
return fmt.Errorf("unknown menutype: %s", menuType)
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ func main() {
|
||||
app.Bind(&Dialog{})
|
||||
app.Bind(&Window{})
|
||||
app.Bind(Menu)
|
||||
app.Bind(Tray)
|
||||
app.Bind(&ContextMenu{})
|
||||
|
||||
err = app.Run()
|
||||
|
||||
@@ -14,7 +14,8 @@ type Tray struct {
|
||||
//dynamicMenuItems map[string]*menu.MenuItem
|
||||
//anotherDynamicMenuCounter int
|
||||
|
||||
trayMenu *menu.TrayMenu
|
||||
trayMenu *menu.TrayMenu
|
||||
secondTrayMenu *menu.TrayMenu
|
||||
|
||||
done bool
|
||||
}
|
||||
@@ -53,6 +54,22 @@ func (t *Tray) WailsInit(runtime *wails.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tray) showWindow(_ *menu.CallbackData) {
|
||||
t.runtime.Window.Show()
|
||||
}
|
||||
|
||||
func (t *Tray) hideWindow(_ *menu.CallbackData) {
|
||||
t.runtime.Window.Hide()
|
||||
}
|
||||
|
||||
func (t *Tray) unminimiseWindow(_ *menu.CallbackData) {
|
||||
t.runtime.Window.Unminimise()
|
||||
}
|
||||
|
||||
func (t *Tray) minimiseWindow(_ *menu.CallbackData) {
|
||||
t.runtime.Window.Minimise()
|
||||
}
|
||||
|
||||
func (t *Tray) WailsShutdown() {
|
||||
t.done = true
|
||||
}
|
||||
@@ -137,14 +154,24 @@ func (t *Tray) createTrayMenus() []*menu.TrayMenu {
|
||||
trayMenu := &menu.TrayMenu{}
|
||||
trayMenu.Label = "Test Tray Label"
|
||||
trayMenu.Menu = menu.NewMenuFromItems(
|
||||
menu.Text("Show Window", "Show Window", nil, nil),
|
||||
menu.Text("Hide Window", "Hide Window", nil, nil),
|
||||
menu.Text("Minimise Window", "Minimise Window", nil, nil),
|
||||
menu.Text("Unminimise Window", "Unminimise Window", nil, nil),
|
||||
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),
|
||||
)
|
||||
t.trayMenu = 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),
|
||||
)
|
||||
t.secondTrayMenu = secondTrayMenu
|
||||
return []*menu.TrayMenu{
|
||||
trayMenu,
|
||||
secondTrayMenu,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user