From fbee9ba24068848e44144738dccc15324fea610a Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Fri, 8 Jan 2021 06:28:51 +1100 Subject: [PATCH] Support UpdateContextMenus. Submenus are now *menu.Menu. Tidy up++ --- v2/internal/ffenestri/contextmenus_darwin.h | 4 -- v2/internal/ffenestri/ffenestri.go | 32 ++++++++++++- v2/internal/ffenestri/ffenestri_client.go | 6 +-- v2/internal/ffenestri/ffenestri_darwin.c | 24 +++++----- v2/internal/ffenestri/ffenestri_darwin.go | 23 +-------- v2/internal/ffenestri/menu.go | 2 +- v2/internal/ffenestri/menu_darwin.h | 8 +++- v2/internal/subsystem/contextmenus.go | 2 +- v2/internal/subsystem/menu.go | 2 +- v2/internal/subsystem/tray.go | 2 +- v2/pkg/menu/menu.go | 10 ++++ v2/pkg/menu/menuitem.go | 53 +++++++++++---------- v2/test/kitchensink/contextmenus.go | 15 +++++- v2/test/kitchensink/menu.go | 42 ++++++++-------- 14 files changed, 133 insertions(+), 92 deletions(-) diff --git a/v2/internal/ffenestri/contextmenus_darwin.h b/v2/internal/ffenestri/contextmenus_darwin.h index bb0ef896..e070912a 100644 --- a/v2/internal/ffenestri/contextmenus_darwin.h +++ b/v2/internal/ffenestri/contextmenus_darwin.h @@ -207,8 +207,6 @@ void ProcessContextMenus(ContextMenuStore* store) { void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData) { - printf("Show context menu '%s'. Also got data '%s'.\n\n", contextMenuID, contextMenuData); - // If no context menu ID was given, abort if( contextMenuID == NULL ) { return; @@ -241,8 +239,6 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context contextMenu->nsmenu = GetMenu(contextMenu->menu); } - printf("\n\nContext menu NSMenu = %p\n\n", contextMenu->menu->menu); - // Show popup msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView); diff --git a/v2/internal/ffenestri/ffenestri.go b/v2/internal/ffenestri/ffenestri.go index f23c6a4e..e1536cfc 100644 --- a/v2/internal/ffenestri/ffenestri.go +++ b/v2/internal/ffenestri/ffenestri.go @@ -1,6 +1,8 @@ package ffenestri import ( + "encoding/json" + "github.com/wailsapp/wails/v2/pkg/menu" "runtime" "strings" "unsafe" @@ -152,7 +154,10 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug dispatcher = incomingDispatcher.RegisterClient(newClient(a)) // Process platform settings - a.processPlatformSettings() + err := a.processPlatformSettings() + if err != nil { + return err + } // Check we could initialise the application if app != nil { @@ -175,3 +180,28 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug func messageFromWindowCallback(data *C.char) { dispatcher.DispatchMessage(C.GoString(data)) } + +type ProcessedContextMenu struct { + ID string + ProcessedMenu *ProcessedMenu +} + +func processContextMenus(contextMenus *menu.ContextMenus) (string, error) { + var processedContextMenus []*ProcessedContextMenu + + // We need to iterate each context menu and pre-process it + for contextMenuID, contextMenu := range contextMenus.Items { + thisContextMenu := &ProcessedContextMenu{ + ID: contextMenuID, + ProcessedMenu: NewProcessedMenu(contextMenu), + } + processedContextMenus = append(processedContextMenus, thisContextMenu) + } + + contextMenusJSON, err := json.Marshal(processedContextMenus) + if err != nil { + return "", err + } + + return string(contextMenusJSON), nil +} diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go index 3a9def77..df96c75b 100644 --- a/v2/internal/ffenestri/ffenestri_client.go +++ b/v2/internal/ffenestri/ffenestri_client.go @@ -233,10 +233,10 @@ func (c *Client) UpdateContextMenus(contextMenus *menu.ContextMenus) { return } // Process the menu - contextMenusJSON, err := json.Marshal(contextMenus) + contextMenusJSON, err := processContextMenus(contextMenus) if err != nil { - c.app.logger.Error("Error processing updated Context Menus: %s", err.Error()) + c.app.logger.Error("Error processing updated Context Menu: %s", err.Error()) return } - C.UpdateContextMenus(c.app.app, c.app.string2CString(string(contextMenusJSON))) + C.UpdateContextMenus(c.app.app, c.app.string2CString(contextMenusJSON)) } diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index 930c75bc..5a72c867 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -1805,17 +1805,19 @@ void UpdateTray(struct Application *app, const char *trayMenuAsJSON) { } void UpdateContextMenus(struct Application *app, const char *contextMenusAsJSON) { -// ON_MAIN_THREAD ( -// -// dumpContextMenus(app); -// -// // Free up memory -// destroyContextMenus(app); -// -// // Set the context menu JSON -// app->contextMenusAsJSON = contextMenusAsJSON; -// parseContextMenus(app); -// ); + + ON_MAIN_THREAD ( + + // Free up memory + DeleteContextMenuStore(app->contextMenuStore); + + // Recreate Context Menus + app->contextMenuStore = NewContextMenuStore(contextMenusAsJSON); + + // Process them + ProcessContextMenus(app->contextMenuStore); + ); + } void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) { diff --git a/v2/internal/ffenestri/ffenestri_darwin.go b/v2/internal/ffenestri/ffenestri_darwin.go index 85de136c..11c797b4 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.go +++ b/v2/internal/ffenestri/ffenestri_darwin.go @@ -24,7 +24,6 @@ extern void SetContextMenus(void *, const char *); import "C" import ( "encoding/json" - "fmt" "github.com/wailsapp/wails/v2/pkg/options" ) @@ -119,29 +118,11 @@ func (a *Application) processPlatformSettings() error { // Process context menus contextMenus := options.GetContextMenus(a.config) if contextMenus != nil { - - type ProcessedContextMenu struct { - ID string - ProcessedMenu *ProcessedMenu - } - - var processedContextMenus []*ProcessedContextMenu - - // We need to iterate each context menu and pre-process it - for contextMenuID, contextMenu := range contextMenus.Items { - thisContextMenu := &ProcessedContextMenu{ - ID: contextMenuID, - ProcessedMenu: NewProcessedMenu(contextMenu), - } - processedContextMenus = append(processedContextMenus, thisContextMenu ) - } - - contextMenusJSON, err := json.Marshal(processedContextMenus) - fmt.Printf("\n\nCONTEXT MENUS:\n %+v\n\n", string(contextMenusJSON)) + contextMenusJSON, err := processContextMenus(contextMenus) if err != nil { return err } - C.SetContextMenus(a.app, a.string2CString(string(contextMenusJSON))) + C.SetContextMenus(a.app, a.string2CString(contextMenusJSON)) } return nil diff --git a/v2/internal/ffenestri/menu.go b/v2/internal/ffenestri/menu.go index 8216b0d6..f24c9ce8 100644 --- a/v2/internal/ffenestri/menu.go +++ b/v2/internal/ffenestri/menu.go @@ -51,7 +51,7 @@ func (p *ProcessedMenu) processMenuItem(item *menu.MenuItem) { p.finaliseRadioGroup() // Process each submenu item - for _, subitem := range item.SubMenu { + for _, subitem := range item.SubMenu.Items { p.processMenuItem(subitem) } case menu.RadioType: diff --git a/v2/internal/ffenestri/menu_darwin.h b/v2/internal/ffenestri/menu_darwin.h index 0d1c7204..41d414f6 100644 --- a/v2/internal/ffenestri/menu_darwin.h +++ b/v2/internal/ffenestri/menu_darwin.h @@ -652,9 +652,15 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) { msg(thisMenuItem, s("setSubmenu:"), thisMenu); msg(parentMenu, s("addItem:"), thisMenuItem); + JsonNode *submenuItems = json_find_member(submenu, "Items"); + // If we have no items, just return + if ( submenuItems == NULL ) { + return; + } + // Loop over submenu items JsonNode *item; - json_foreach(item, submenu) { + json_foreach(item, submenuItems) { // Get item label processMenuItem(menu, thisMenu, item); } diff --git a/v2/internal/subsystem/contextmenus.go b/v2/internal/subsystem/contextmenus.go index ea058e70..652b70cd 100644 --- a/v2/internal/subsystem/contextmenus.go +++ b/v2/internal/subsystem/contextmenus.go @@ -158,7 +158,7 @@ func (c *ContextMenus) processContextMenus(contextMenus *menu.ContextMenus) { func (c *ContextMenus) processMenuItem(item *menu.MenuItem) { if item.SubMenu != nil { - for _, submenuitem := range item.SubMenu { + for _, submenuitem := range item.SubMenu.Items { c.processMenuItem(submenuitem) } return diff --git a/v2/internal/subsystem/menu.go b/v2/internal/subsystem/menu.go index 98f843ae..a700a17d 100644 --- a/v2/internal/subsystem/menu.go +++ b/v2/internal/subsystem/menu.go @@ -153,7 +153,7 @@ func (m *Menu) processMenu(applicationMenu *menu.Menu) { func (m *Menu) processMenuItem(item *menu.MenuItem) { if item.SubMenu != nil { - for _, submenuitem := range item.SubMenu { + for _, submenuitem := range item.SubMenu.Items { m.processMenuItem(submenuitem) } return diff --git a/v2/internal/subsystem/tray.go b/v2/internal/subsystem/tray.go index 8baee96d..99165707 100644 --- a/v2/internal/subsystem/tray.go +++ b/v2/internal/subsystem/tray.go @@ -160,7 +160,7 @@ func (t *Tray) processMenu(trayMenu *menu.Menu) { func (t *Tray) processMenuItem(item *menu.MenuItem) { if item.SubMenu != nil { - for _, submenuitem := range item.SubMenu { + for _, submenuitem := range item.SubMenu.Items { t.processMenuItem(submenuitem) } return diff --git a/v2/pkg/menu/menu.go b/v2/pkg/menu/menu.go index 14fce80f..137584a2 100644 --- a/v2/pkg/menu/menu.go +++ b/v2/pkg/menu/menu.go @@ -12,6 +12,10 @@ func (m *Menu) Append(item *MenuItem) { m.Items = append(m.Items, item) } +func (m *Menu) Prepend(item *MenuItem) { + m.Items = append([]*MenuItem{item}, m.Items...) +} + func NewMenuFromItems(first *MenuItem, rest ...*MenuItem) *Menu { var result = NewMenu() @@ -49,3 +53,9 @@ func (m *Menu) RemoveByID(id string) bool { } return false } + +func (m *Menu) setParent(menuItem *MenuItem) { + for _, item := range m.Items { + item.parent = menuItem + } +} diff --git a/v2/pkg/menu/menuitem.go b/v2/pkg/menu/menuitem.go index d3b14fc9..078190b2 100644 --- a/v2/pkg/menu/menuitem.go +++ b/v2/pkg/menu/menuitem.go @@ -21,7 +21,8 @@ type MenuItem struct { // Checked indicates if the item is selected (used by Checkbox and Radio types only) Checked bool // Submenu contains a list of menu items that will be shown as a submenu - SubMenu []*MenuItem `json:"SubMenu,omitempty"` + //SubMenu []*MenuItem `json:"SubMenu,omitempty"` + SubMenu *Menu `json:"SubMenu,omitempty"` // Foreground colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red Foreground int @@ -48,7 +49,7 @@ func (m *MenuItem) Append(item *MenuItem) bool { return false } item.parent = m - m.SubMenu = append(m.SubMenu, item) + m.SubMenu.Append(item) return true } @@ -61,7 +62,7 @@ func (m *MenuItem) Prepend(item *MenuItem) bool { return false } item.parent = m - m.SubMenu = append([]*MenuItem{item}, m.SubMenu...) + m.SubMenu.Prepend(item) return true } @@ -72,8 +73,13 @@ func (m *MenuItem) getByID(id string) *MenuItem { return m } + // If we have no submenu then exit early + if m.SubMenu == nil { + return nil + } + // Check submenus - for _, submenu := range m.SubMenu { + for _, submenu := range m.SubMenu.Items { result := submenu.getByID(id) if result != nil { return result @@ -85,9 +91,14 @@ func (m *MenuItem) getByID(id string) *MenuItem { func (m *MenuItem) removeByID(id string) bool { - for index, item := range m.SubMenu { + // If we have no submenu, return + if m.SubMenu == nil { + return false + } + + for index, item := range m.SubMenu.Items { if item.ID == id { - m.SubMenu = append(m.SubMenu[:index], m.SubMenu[index+1:]...) + m.SubMenu.Items = append(m.SubMenu.Items[:index], m.SubMenu.Items[index+1:]...) return true } if item.isSubMenu() { @@ -181,7 +192,7 @@ func (m *MenuItem) getItemIndex(target *MenuItem) int { } // hunt down that bad boy - for index, item := range m.SubMenu { + for index, item := range m.SubMenu.Items { if item == target { return index } @@ -196,7 +207,7 @@ func (m *MenuItem) getItemIndex(target *MenuItem) int { func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool { // If index is OOB, return false - if index > len(m.SubMenu) { + if index > len(m.SubMenu.Items) { return false } @@ -204,13 +215,13 @@ func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool { target.parent = m // If index is last item, then just regular append - if index == len(m.SubMenu) { - m.SubMenu = append(m.SubMenu, target) + if index == len(m.SubMenu.Items) { + m.SubMenu.Items = append(m.SubMenu.Items, target) return true } - m.SubMenu = append(m.SubMenu[:index+1], m.SubMenu[index:]...) - m.SubMenu[index] = target + m.SubMenu.Items = append(m.SubMenu.Items[:index+1], m.SubMenu.Items[index:]...) + m.SubMenu.Items[index] = target return true } @@ -254,34 +265,28 @@ func Checkbox(label string, id string, checked bool, accelerator *keys.Accelerat } // SubMenu is a helper to create Submenus -func SubMenu(label string, items []*MenuItem) *MenuItem { +func SubMenu(label string, menu *Menu) *MenuItem { result := &MenuItem{ Label: label, - SubMenu: items, + SubMenu: menu, Type: SubmenuType, } - // Fix up parent pointers - for _, item := range items { - item.parent = result - } + menu.setParent(result) return result } // SubMenuWithID is a helper to create Submenus with an ID -func SubMenuWithID(label string, id string, items []*MenuItem) *MenuItem { +func SubMenuWithID(label string, id string, menu *Menu) *MenuItem { result := &MenuItem{ Label: label, - SubMenu: items, + SubMenu: menu, ID: id, Type: SubmenuType, } - // Fix up parent pointers - for _, item := range items { - item.parent = result - } + menu.setParent(result) return result } diff --git a/v2/test/kitchensink/contextmenus.go b/v2/test/kitchensink/contextmenus.go index 0423e61d..a6212e05 100644 --- a/v2/test/kitchensink/contextmenus.go +++ b/v2/test/kitchensink/contextmenus.go @@ -22,7 +22,6 @@ func (c *ContextMenu) WailsInit(runtime *wails.Runtime) error { // Setup Menu Listeners c.runtime.ContextMenu.On("Test Context Menu", func(mi *menu.MenuItem, contextData string) { - fmt.Printf("\n\nContext Data = '%s'\n\n", contextData) c.lock.Lock() c.counter++ mi.Label = fmt.Sprintf("Clicked %d times", c.counter) @@ -35,6 +34,18 @@ func (c *ContextMenu) WailsInit(runtime *wails.Runtime) error { func createContextMenus() *menu.ContextMenus { result := menu.NewContextMenus() - result.AddMenu("test", menu.NewMenuFromItems(menu.Text("Clicked 0 times", "Test Context Menu", nil))) + result.AddMenu("test", menu.NewMenuFromItems( + menu.Text("Clicked 0 times", "Test Context Menu", nil), + menu.Separator(), + menu.Checkbox("I am a checkbox", "checkbox", false, nil), + menu.Separator(), + menu.Radio("Radio Option 1", "Radio Option 1", true, nil), + menu.Radio("Radio Option 2", "Radio Option 2", false, nil), + menu.Radio("Radio Option 3", "Radio Option 3", false, nil), + menu.Separator(), + menu.SubMenu("A Submenu", menu.NewMenuFromItems( + menu.Text("Hello", "Hello", nil), + )), + )) return result } diff --git a/v2/test/kitchensink/menu.go b/v2/test/kitchensink/menu.go index 7a89f0c1..cf98b334 100644 --- a/v2/test/kitchensink/menu.go +++ b/v2/test/kitchensink/menu.go @@ -124,13 +124,13 @@ func (m *Menu) removeMenu(_ *menu.MenuItem) { func (m *Menu) createDynamicMenuTwo() { // Create our submenu - dm2 := menu.SubMenu("Dynamic Menus 2", []*menu.MenuItem{ + dm2 := menu.SubMenu("Dynamic Menus 2", menu.NewMenuFromItems( menu.Text("Insert Before Random Menu Item", "Insert Before Random", keys.CmdOrCtrl("]")), menu.Text("Insert After Random Menu Item", "Insert After Random", keys.CmdOrCtrl("[")), menu.Separator(), - }) + )) m.runtime.Menu.On("Insert Before Random", m.insertBeforeRandom) m.runtime.Menu.On("Insert After Random", m.insertAfterRandom) @@ -229,7 +229,7 @@ func createApplicationMenu() *menu.Menu { // Create menu myMenu := menu.DefaultMacMenu() - windowMenu := menu.SubMenu("Test", []*menu.MenuItem{ + windowMenu := menu.SubMenu("Test", menu.NewMenuFromItems( menu.Togglefullscreen(), menu.Minimize(), menu.Zoom(), @@ -244,17 +244,17 @@ func createApplicationMenu() *menu.Menu { menu.Front(), - menu.SubMenu("Test Submenu", []*menu.MenuItem{ + menu.SubMenu("Test Submenu", menu.NewMenuFromItems( menu.Text("Plain text", "plain text", nil), menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil), - menu.SubMenu("Accelerators", []*menu.MenuItem{ - menu.SubMenu("Modifiers", []*menu.MenuItem{ + menu.SubMenu("Accelerators", menu.NewMenuFromItems( + menu.SubMenu("Modifiers", menu.NewMenuFromItems( menu.Text("Shift accelerator", "Shift", keys.Shift("o")), menu.Text("Control accelerator", "Control", keys.Control("o")), menu.Text("Command accelerator", "Command", keys.CmdOrCtrl("o")), menu.Text("Option accelerator", "Option", keys.OptionOrAlt("o")), - }), - menu.SubMenu("System Keys", []*menu.MenuItem{ + )), + menu.SubMenu("System Keys", menu.NewMenuFromItems( menu.Text("Backspace", "Backspace", keys.Key("Backspace")), menu.Text("Tab", "Tab", keys.Key("Tab")), menu.Text("Return", "Return", keys.Key("Return")), @@ -270,8 +270,8 @@ func createApplicationMenu() *menu.Menu { menu.Text("Page Up", "Page Up", keys.Key("Page Up")), menu.Text("Page Down", "Page Down", keys.Key("Page Down")), menu.Text("NumLock", "NumLock", keys.Key("NumLock")), - }), - menu.SubMenu("Function Keys", []*menu.MenuItem{ + )), + menu.SubMenu("Function Keys", menu.NewMenuFromItems( menu.Text("F1", "F1", keys.Key("F1")), menu.Text("F2", "F2", keys.Key("F2")), menu.Text("F3", "F3", keys.Key("F3")), @@ -292,28 +292,28 @@ func createApplicationMenu() *menu.Menu { menu.Text("F18", "F18", keys.Key("F18")), menu.Text("F19", "F19", keys.Key("F19")), menu.Text("F20", "F20", keys.Key("F20")), - }), - menu.SubMenu("Standard Keys", []*menu.MenuItem{ + )), + menu.SubMenu("Standard Keys", menu.NewMenuFromItems( menu.Text("Backtick", "Backtick", keys.Key("`")), menu.Text("Plus", "Plus", keys.Key("+")), - }), - }), - menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", []*menu.MenuItem{ + )), + )), + menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", menu.NewMenuFromItems( menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+")), menu.Separator(), - }), - { + )), + &menu.MenuItem{ Label: "Disabled Menu", Type: menu.TextType, Accelerator: keys.Combo("p", keys.CmdOrCtrlKey, keys.ShiftKey), Disabled: true, }, - { + &menu.MenuItem{ Label: "Hidden Menu", Type: menu.TextType, Hidden: true, }, - { + &menu.MenuItem{ ID: "checkbox-menu 1", Label: "Checkbox Menu 1", Type: menu.CheckboxType, @@ -325,8 +325,8 @@ func createApplicationMenu() *menu.Menu { menu.Radio("😀 Option 1", "😀option-1", true, nil), menu.Radio("😺 Option 2", "option-2", false, nil), menu.Radio("❤️ Option 3", "option-3", false, nil), - }), - }) + )), + )) myMenu.Append(windowMenu) return myMenu