mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 22:50:59 +08:00
Support Menu updates!
This commit is contained in:
parent
7347d2caa2
commit
8053357d99
@ -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 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 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 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 UpdateTray(void *app, char *menuAsJSON);
|
||||||
extern void UpdateContextMenus(void *app, char *contextMenusAsJSON);
|
extern void UpdateContextMenus(void *app, char *contextMenusAsJSON);
|
||||||
extern void UpdateTrayLabel(void *app, const char *label);
|
extern void UpdateTrayLabel(void *app, const char *label);
|
||||||
|
@ -186,20 +186,8 @@ func (c *Client) DarkModeEnabled(callbackID string) {
|
|||||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateMenu(menu *menu.Menu) {
|
func (c *Client) UpdateMenu(menuJSON string) {
|
||||||
|
C.SetApplicationMenu(c.app.app, c.app.string2CString(menuJSON))
|
||||||
// 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) UpdateTray(menu *menu.Menu) {
|
func (c *Client) UpdateTray(menu *menu.Menu) {
|
||||||
|
@ -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) {
|
int releaseNSObject(void *const context, struct hashmap_element_s *const e) {
|
||||||
msg(e->data, s("release"));
|
msg(e->data, s("release"));
|
||||||
@ -1121,10 +1059,7 @@ void SetDebug(void *applicationPointer, int flag) {
|
|||||||
debug = 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
|
// SetTray sets the initial tray menu for the application
|
||||||
void SetTray(struct Application *app, const char *trayMenuAsJSON, const char *trayLabel, const char *trayIconName) {
|
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
|
// updateMenu replaces the current menu with the given one
|
||||||
void UpdateMenu(struct Application *app, const char *menuAsJSON) {
|
void updateMenu(struct Application *app, const char *menuAsJSON) {
|
||||||
Debug(app, "Menu is now: %s", menuAsJSON);
|
Debug(app, "Menu is now: %s", menuAsJSON);
|
||||||
ON_MAIN_THREAD (
|
ON_MAIN_THREAD (
|
||||||
DeleteMenu(app->applicationMenu);
|
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) {
|
//void dumpContextMenus(struct Application *app) {
|
||||||
// dumpHashmap("menuItemMapForContextMenus", &menuItemMapForContextMenus);
|
// dumpHashmap("menuItemMapForContextMenus", &menuItemMapForContextMenus);
|
||||||
// printf("&menuItemMapForContextMenus = %p\n", &menuItemMapForContextMenus);
|
// printf("&menuItemMapForContextMenus = %p\n", &menuItemMapForContextMenus);
|
||||||
@ -2063,4 +2009,68 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
MEMFREE(internalCode);
|
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
|
#endif
|
||||||
|
@ -4,6 +4,7 @@ package ffenestri
|
|||||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||||
|
|
||||||
|
#include "ffenestri.h"
|
||||||
#include "ffenestri_darwin.h"
|
#include "ffenestri_darwin.h"
|
||||||
|
|
||||||
extern void TitlebarAppearsTransparent(void *);
|
extern void TitlebarAppearsTransparent(void *);
|
||||||
@ -16,7 +17,6 @@ extern void DisableFrame(void *);
|
|||||||
extern void SetAppearance(void *, const char *);
|
extern void SetAppearance(void *, const char *);
|
||||||
extern void WebviewIsTransparent(void *);
|
extern void WebviewIsTransparent(void *);
|
||||||
extern void WindowBackgroundIsTranslucent(void *);
|
extern void WindowBackgroundIsTranslucent(void *);
|
||||||
extern void SetMenu(void *, const char *);
|
|
||||||
extern void SetTray(void *, const char *, const char *, const char *);
|
extern void SetTray(void *, const char *, const char *, const char *);
|
||||||
extern void SetContextMenus(void *, const char *);
|
extern void SetContextMenus(void *, const char *);
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ func (a *Application) processPlatformSettings() error {
|
|||||||
//applicationMenu := options.GetApplicationMenu(a.config)
|
//applicationMenu := options.GetApplicationMenu(a.config)
|
||||||
applicationMenu := a.menuManager.GetApplicationMenuJSON()
|
applicationMenu := a.menuManager.GetApplicationMenuJSON()
|
||||||
if applicationMenu != "" {
|
if applicationMenu != "" {
|
||||||
C.SetMenu(a.app, a.string2CString(applicationMenu))
|
C.SetApplicationMenu(a.app, a.string2CString(applicationMenu))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process tray
|
// Process tray
|
||||||
|
@ -9,7 +9,10 @@
|
|||||||
#include "contextmenustore_darwin.h"
|
#include "contextmenustore_darwin.h"
|
||||||
|
|
||||||
enum MenuItemType {Text = 0, Checkbox = 1, Radio = 2};
|
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 *);
|
extern void messageFromWindowCallback(const char *);
|
||||||
|
|
||||||
@ -133,14 +136,17 @@ void DeleteMenu(Menu *menu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates a JSON message for the given menuItemID and data
|
// 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();
|
JsonNode *jsonObject = json_mkobject();
|
||||||
json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID));
|
json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID));
|
||||||
|
json_append_member(jsonObject, "menuType", json_mkstring(MenuTypeAsString[(int)menuType]));
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
json_append_member(jsonObject, "data", json_mkstring(data));
|
json_append_member(jsonObject, "data", json_mkstring(data));
|
||||||
}
|
}
|
||||||
const char *result = json_encode(jsonObject);
|
const char *payload = json_encode(jsonObject);
|
||||||
json_delete(jsonObject);
|
json_delete(jsonObject);
|
||||||
|
const char *result = concat("MC", payload);
|
||||||
|
MEMFREE(payload);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,19 +183,19 @@ void menuItemCallback(id self, SEL cmd, id sender) {
|
|||||||
msg(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
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
|
// Generate message to send to backend
|
||||||
if( callbackData->menu->menuType == ApplicationMenuType ) {
|
if( menuType == ContextMenuType ) {
|
||||||
const char *clickMessage = createMenuClickedMessage(callbackData->menuID, NULL);
|
|
||||||
message = concat("MC", clickMessage);
|
|
||||||
MEMFREE(clickMessage);
|
|
||||||
} else if( callbackData->menu->menuType == ContextMenuType ) {
|
|
||||||
// Get the context menu data from the menu
|
// Get the context menu data from the menu
|
||||||
ContextMenuStore* store = (ContextMenuStore*) callbackData->menu->parentData;
|
ContextMenuStore* store = (ContextMenuStore*) callbackData->menu->parentData;
|
||||||
const char *clickMessage = createMenuClickedMessage(callbackData->menuID, store->contextMenuData);
|
data = store->contextMenuData;
|
||||||
message = concat("XC", clickMessage);
|
|
||||||
MEMFREE(clickMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message = createMenuClickedMessage(menuID, data, menuType);
|
||||||
|
|
||||||
// TODO: Add other menu types here!
|
// TODO: Add other menu types here!
|
||||||
|
|
||||||
// Notify the backend
|
// Notify the backend
|
||||||
|
@ -3,12 +3,17 @@ package menumanager
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MenuItemMap holds a mapping between menuIDs and menu items
|
// MenuItemMap holds a mapping between menuIDs and menu items
|
||||||
type MenuItemMap struct {
|
type MenuItemMap struct {
|
||||||
idToMenuItemMap map[string]*menu.MenuItem
|
idToMenuItemMap map[string]*menu.MenuItem
|
||||||
menuItemToIDMap map[*menu.MenuItem]string
|
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 {
|
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) {
|
func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) {
|
||||||
|
|
||||||
if item.SubMenu != nil {
|
if item.SubMenu != nil {
|
||||||
@ -46,9 +60,13 @@ func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a unique ID for this menu item
|
// Create a unique ID for this menu item
|
||||||
menuID := fmt.Sprintf("%d", len(m.idToMenuItemMap))
|
menuID := m.generateMenuID()
|
||||||
|
|
||||||
// Store references
|
// Store references
|
||||||
m.idToMenuItemMap[menuID] = item
|
m.idToMenuItemMap[menuID] = item
|
||||||
m.menuItemToIDMap[item] = menuID
|
m.menuItemToIDMap[item] = menuID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MenuItemMap) getMenuItemByID(menuId string) *menu.MenuItem {
|
||||||
|
return m.idToMenuItemMap[menuId]
|
||||||
|
}
|
||||||
|
@ -11,13 +11,13 @@ type Manager struct {
|
|||||||
applicationMenu *menu.Menu
|
applicationMenu *menu.Menu
|
||||||
applicationMenuJSON string
|
applicationMenuJSON string
|
||||||
|
|
||||||
// Our menu mappings
|
// Our application menu mappings
|
||||||
menuItemMap *MenuItemMap
|
applicationMenuItemMap *MenuItemMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
menuItemMap: NewMenuItemMap(),
|
applicationMenuItemMap: NewMenuItemMap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,13 @@ func (m *Manager) SetApplicationMenu(applicationMenu *menu.Menu) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
m.applicationMenu = applicationMenu
|
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()
|
return m.processApplicationMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,10 +40,20 @@ func (m *Manager) GetApplicationMenuJSON() string {
|
|||||||
return m.applicationMenuJSON
|
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 {
|
func (m *Manager) processApplicationMenu() error {
|
||||||
|
|
||||||
// Process the menu
|
// Process the menu
|
||||||
processedApplicationMenu := m.NewWailsMenu(m.applicationMenu)
|
processedApplicationMenu := m.NewWailsMenu(m.applicationMenuItemMap, m.applicationMenu)
|
||||||
applicationMenuJSON, err := processedApplicationMenu.AsJSON()
|
applicationMenuJSON, err := processedApplicationMenu.AsJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -46,14 +62,27 @@ func (m *Manager) processApplicationMenu() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) getMenuItemByID(menuId string) *menu.MenuItem {
|
func (m *Manager) getMenuItemByID(menuMap *MenuItemMap, menuId string) *menu.MenuItem {
|
||||||
return m.menuItemMap.idToMenuItemMap[menuId]
|
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
|
// Get the menu item
|
||||||
menuItem := m.getMenuItemByID(menuID)
|
menuItem := menuItemMap.getMenuItemByID(menuID)
|
||||||
if menuItem == nil {
|
if menuItem == nil {
|
||||||
return fmt.Errorf("Cannot process menuid %s - unknown", menuID)
|
return fmt.Errorf("Cannot process menuid %s - unknown", menuID)
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,9 @@ type ProcessedMenuItem struct {
|
|||||||
Background int
|
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{
|
result := &ProcessedMenuItem{
|
||||||
ID: ID,
|
ID: ID,
|
||||||
Label: menuItem.Label,
|
Label: menuItem.Label,
|
||||||
@ -50,7 +50,7 @@ func (m *Manager) NewProcessedMenuItem(menuItem *menu.MenuItem) *ProcessedMenuIt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if menuItem.SubMenu != nil {
|
if menuItem.SubMenu != nil {
|
||||||
result.SubMenu = m.NewProcessedMenu(menuItem.SubMenu)
|
result.SubMenu = m.NewProcessedMenu(menuItemMap, menuItem.SubMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -60,11 +60,11 @@ type ProcessedMenu struct {
|
|||||||
Items []*ProcessedMenuItem
|
Items []*ProcessedMenuItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) NewProcessedMenu(menu *menu.Menu) *ProcessedMenu {
|
func (m *Manager) NewProcessedMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *ProcessedMenu {
|
||||||
|
|
||||||
result := &ProcessedMenu{}
|
result := &ProcessedMenu{}
|
||||||
for _, item := range menu.Items {
|
for _, item := range menu.Items {
|
||||||
processedMenuItem := m.NewProcessedMenuItem(item)
|
processedMenuItem := m.NewProcessedMenuItem(menuItemMap, item)
|
||||||
result.Items = append(result.Items, processedMenuItem)
|
result.Items = append(result.Items, processedMenuItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,11 +85,11 @@ type RadioGroup struct {
|
|||||||
Length int
|
Length int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) NewWailsMenu(menu *menu.Menu) *WailsMenu {
|
func (m *Manager) NewWailsMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *WailsMenu {
|
||||||
result := &WailsMenu{}
|
result := &WailsMenu{}
|
||||||
|
|
||||||
// Process the menus
|
// Process the menus
|
||||||
result.Menu = m.NewProcessedMenu(menu)
|
result.Menu = m.NewProcessedMenu(menuItemMap, menu)
|
||||||
|
|
||||||
// Process the radio groups
|
// Process the radio groups
|
||||||
result.processRadioGroups()
|
result.processRadioGroups()
|
||||||
|
@ -32,7 +32,7 @@ type Client interface {
|
|||||||
WindowUnFullscreen()
|
WindowUnFullscreen()
|
||||||
WindowSetColour(colour int)
|
WindowSetColour(colour int)
|
||||||
DarkModeEnabled(callbackID string)
|
DarkModeEnabled(callbackID string)
|
||||||
UpdateMenu(menu *menu.Menu)
|
UpdateMenu(menuJSON string)
|
||||||
UpdateTray(menu *menu.Menu)
|
UpdateTray(menu *menu.Menu)
|
||||||
UpdateTrayLabel(label string)
|
UpdateTrayLabel(label string)
|
||||||
UpdateTrayIcon(name string)
|
UpdateTrayIcon(name string)
|
||||||
|
@ -449,11 +449,11 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|||||||
|
|
||||||
command := splitTopic[1]
|
command := splitTopic[1]
|
||||||
switch command {
|
switch command {
|
||||||
case "update":
|
case "updateappmenu":
|
||||||
|
|
||||||
updatedMenu, ok := result.Data().(*menu.Menu)
|
updatedMenu, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'menufrontend:update' : %#v",
|
d.logger.Error("Invalid data for 'menufrontend:updateappmenu' : %#v",
|
||||||
result.Data())
|
result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ func (m *menuRuntime) On(menuID string, callback func(*menu.MenuItem)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *menuRuntime) Update() {
|
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 {
|
func (m *menuRuntime) GetByID(menuID string) *menu.MenuItem {
|
||||||
|
@ -99,18 +99,19 @@ func (m *Menu) Start() error {
|
|||||||
|
|
||||||
type ClickCallbackMessage struct {
|
type ClickCallbackMessage struct {
|
||||||
MenuItemID string `json:"menuItemID"`
|
MenuItemID string `json:"menuItemID"`
|
||||||
|
MenuType string `json:"menuType"`
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var callbackData ClickCallbackMessage
|
var callbackData ClickCallbackMessage
|
||||||
message := []byte(menuMessage.Data().(string))
|
payload := []byte(menuMessage.Data().(string))
|
||||||
err := json.Unmarshal(message, &callbackData)
|
err := json.Unmarshal(payload, &callbackData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Error("%s", err.Error())
|
m.logger.Error("%s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.menuManager.ProcessClick(callbackData.MenuItemID, callbackData.Data)
|
err = m.menuManager.ProcessClick(callbackData.MenuItemID, callbackData.Data, callbackData.MenuType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Trace("%s", err.Error())
|
m.logger.Trace("%s", err.Error())
|
||||||
}
|
}
|
||||||
@ -121,12 +122,17 @@ func (m *Menu) Start() error {
|
|||||||
m.listeners[id] = append(m.listeners[id], listenerDetails.Callback)
|
m.listeners[id] = append(m.listeners[id], listenerDetails.Callback)
|
||||||
|
|
||||||
// Make sure we catch any menu updates
|
// 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)
|
//updatedMenu := menuMessage.Data().(*menu.Menu)
|
||||||
//m.processMenu(updatedMenu)
|
//m.processMenu(updatedMenu)
|
||||||
//
|
//
|
||||||
//// Notify frontend of menu change
|
//// Notify frontend of menu change
|
||||||
//m.bus.Publish("menufrontend:update", updatedMenu)
|
m.bus.Publish("menufrontend:updateappmenu", updatedMenu)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m.logger.Error("unknown menu message: %+v", menuMessage)
|
m.logger.Error("unknown menu message: %+v", menuMessage)
|
||||||
|
@ -148,6 +148,12 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
// Default go build command
|
// Default go build command
|
||||||
commands := slicer.String([]string{"build"})
|
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
|
// TODO: Work out if we can make this more efficient
|
||||||
// We need to do a full build as CGO doesn't detect updates
|
// 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
|
// 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
|
// Get application build directory
|
||||||
appDir := options.BuildDirectory
|
appDir := options.BuildDirectory
|
||||||
err := cleanBuildDirectory(options)
|
err := cleanBuildDirectory(options)
|
||||||
@ -210,6 +211,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
options.CompiledBinary = compiledBinary
|
options.CompiledBinary = compiledBinary
|
||||||
|
|
||||||
// Create the command
|
// Create the command
|
||||||
|
fmt.Printf("Compile command: %+v", commands.AsSlice())
|
||||||
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
||||||
|
|
||||||
// Set the directory
|
// Set the directory
|
||||||
|
@ -19,6 +19,9 @@ type Menu struct {
|
|||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
dynamicMenuItems map[string]*menu.MenuItem
|
dynamicMenuItems map[string]*menu.MenuItem
|
||||||
anotherDynamicMenuCounter int
|
anotherDynamicMenuCounter int
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
removeMenuItem *menu.MenuItem
|
||||||
}
|
}
|
||||||
|
|
||||||
// WailsInit is called at application startup
|
// WailsInit is called at application startup
|
||||||
@ -26,26 +29,6 @@ func (m *Menu) WailsInit(runtime *wails.Runtime) error {
|
|||||||
// Perform your setup here
|
// Perform your setup here
|
||||||
m.runtime = runtime
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +42,14 @@ func (m *Menu) decrementcounter() int {
|
|||||||
return m.dynamicMenuCounter
|
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
|
// Lock because this method will be called in a gorouting
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
// Get this menu's parent
|
// Get this menu's parent
|
||||||
|
mi := data.MenuItem
|
||||||
parent := mi.Parent()
|
parent := mi.Parent()
|
||||||
counter := m.incrementcounter()
|
counter := m.incrementcounter()
|
||||||
menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
|
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 this is the first dynamic menu added, let's add a remove menu item
|
||||||
if counter == 1 {
|
if counter == 1 {
|
||||||
removeMenu := menu.Text("Remove "+menuText,
|
m.removeMenuItem = menu.Text("Remove "+menuText, "Remove Last Item", keys.CmdOrCtrl("-"), m.removeMenu)
|
||||||
"Remove Last Item", keys.CmdOrCtrl("-"), nil)
|
parent.Prepend(m.removeMenuItem)
|
||||||
parent.Prepend(removeMenu)
|
|
||||||
m.runtime.Menu.On("Remove Last Item", m.removeMenu)
|
|
||||||
} else {
|
} else {
|
||||||
removeMenu := m.runtime.Menu.GetByID("Remove Last Item")
|
removeMenu := m.runtime.Menu.GetByID("Remove Last Item")
|
||||||
// Test if the remove menu hasn't already been removed in another thread
|
// 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()
|
m.runtime.Menu.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) removeMenu(_ *menu.MenuItem) {
|
func (m *Menu) removeMenu(_ *menu.CallbackData) {
|
||||||
|
//
|
||||||
// Lock because this method will be called in a goroutine
|
//// Lock because this method will be called in a goroutine
|
||||||
m.lock.Lock()
|
//m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
//defer m.lock.Unlock()
|
||||||
|
//
|
||||||
// Get the id of the last dynamic menu
|
//// Remove the last menu item by ID
|
||||||
menuID := "Dynamic Menu Item " + strconv.Itoa(m.dynamicMenuCounter)
|
//m.runtime.Menu.RemoveMenuItem(menuID)
|
||||||
|
//
|
||||||
// Remove the last menu item by ID
|
//// Update the counter
|
||||||
m.runtime.Menu.RemoveByID(menuID)
|
//counter := m.decrementcounter()
|
||||||
|
//
|
||||||
// Update the counter
|
//// If we deleted the last dynamic menu, remove the "Remove Last Item" menu
|
||||||
counter := m.decrementcounter()
|
//if counter == 0 {
|
||||||
|
// m.runtime.Menu.RemoveByID("Remove Last Item")
|
||||||
// If we deleted the last dynamic menu, remove the "Remove Last Item" menu
|
//} else {
|
||||||
if counter == 0 {
|
// // Update label
|
||||||
m.runtime.Menu.RemoveByID("Remove Last Item")
|
// menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
|
||||||
} else {
|
// removeMenu := m.runtime.Menu.GetByID("Remove Last Item")
|
||||||
// Update label
|
// // Test if the remove menu hasn't already been removed in another thread
|
||||||
menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
|
// if removeMenu == nil {
|
||||||
removeMenu := m.runtime.Menu.GetByID("Remove Last Item")
|
// return
|
||||||
// Test if the remove menu hasn't already been removed in another thread
|
// }
|
||||||
if removeMenu == nil {
|
// removeMenu.Label = "Remove " + menuText
|
||||||
return
|
//}
|
||||||
}
|
//
|
||||||
removeMenu.Label = "Remove " + menuText
|
//// parent.Append(menu.Text(menuText, menuText, menu.Key("[")))
|
||||||
}
|
//m.runtime.Menu.Update()
|
||||||
|
|
||||||
// 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
|
// Create our submenu
|
||||||
dm2 := menu.SubMenu("Dynamic Menus 2", menu.NewMenuFromItems(
|
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.SubMenu("Test Submenu", menu.NewMenuFromItems(
|
||||||
menu.Text("Plain text", "plain text", nil, m.processPlainText),
|
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("Accelerators", menu.NewMenuFromItems(
|
||||||
menu.SubMenu("Modifiers", menu.NewMenuFromItems(
|
menu.SubMenu("Modifiers", menu.NewMenuFromItems(
|
||||||
menu.Text("Shift accelerator", "Shift", keys.Shift("o"), nil),
|
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.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.Separator(),
|
||||||
)),
|
)),
|
||||||
&menu.MenuItem{
|
&menu.MenuItem{
|
||||||
@ -325,6 +308,10 @@ func (m *Menu) createApplicationMenu() *menu.Menu {
|
|||||||
Type: menu.CheckboxType,
|
Type: menu.CheckboxType,
|
||||||
Accelerator: keys.CmdOrCtrl("l"),
|
Accelerator: keys.CmdOrCtrl("l"),
|
||||||
Checked: true,
|
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.Checkbox("Checkbox Menu 2", "checkbox-menu 2", false, nil, nil),
|
||||||
menu.Separator(),
|
menu.Separator(),
|
||||||
|
Loading…
Reference in New Issue
Block a user