From b5478e52db353398e6717f9dc8130ed21861b558 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Fri, 7 Oct 2022 20:02:58 +1100 Subject: [PATCH] Support checkbox logic. Support duplicate checkboxes --- .gitignore | 3 +- v2/internal/platform/systray/menu.go | 34 +++++++++++++----- v2/internal/platform/win32/consts.go | 2 ++ v2/internal/platform/win32/menu.go | 9 +++++ v2/pkg/menu/menuitem.go | 53 +++++++++++++++++++--------- 5 files changed, 76 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 6f2284002..bc604b5dd 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ v2/cmd/wails/internal/commands/initialise/templates/testtemplates/ .env /website/static/img/.cache.json -v2/internal/frontend/desktop/darwin/test.xcodeproj \ No newline at end of file +v2/internal/frontend/desktop/darwin/test.xcodeproj +TODO.md \ No newline at end of file diff --git a/v2/internal/platform/systray/menu.go b/v2/internal/platform/systray/menu.go index 685356c49..f54c4bab2 100644 --- a/v2/internal/platform/systray/menu.go +++ b/v2/internal/platform/systray/menu.go @@ -7,10 +7,11 @@ import ( ) type PopupMenu struct { - menu win32.HMENU - parent win32.HWND - menuMapping map[int]*menu.MenuItem - menuData *menu.Menu + menu win32.HMENU + parent win32.HWND + menuMapping map[int]*menu.MenuItem + checkboxItems map[*menu.MenuItem][]int + menuData *menu.Menu } func (p *PopupMenu) buildMenu(parentMenu win32.HMENU, inputMenu *menu.Menu, startindex int) error { @@ -45,6 +46,9 @@ func (p *PopupMenu) buildMenu(parentMenu win32.HMENU, inputMenu *menu.Menu, star } p.menuMapping[itemID] = item + if item.IsCheckbox() { + p.checkboxItems[item] = append(p.checkboxItems[item], itemID) + } ret = win32.AppendMenu(parentMenu, uintptr(flags), uintptr(itemID), item.Label) if ret == false { return errors.New("AppendMenu failed") @@ -60,9 +64,10 @@ func (p *PopupMenu) Update() error { func NewPopupMenu(parent win32.HWND, inputMenu *menu.Menu) (*PopupMenu, error) { result := &PopupMenu{ - parent: parent, - menuData: inputMenu, - menuMapping: make(map[int]*menu.MenuItem), + parent: parent, + menuData: inputMenu, + menuMapping: make(map[int]*menu.MenuItem), + checkboxItems: make(map[*menu.MenuItem][]int), } err := result.Update() return result, err @@ -92,7 +97,20 @@ func (p *PopupMenu) ShowAtCursor() error { func (p *PopupMenu) ProcessCommand(cmdMsgID int) { item := p.menuMapping[cmdMsgID] if item != nil { - item.Click(&menu.CallbackData{MenuItem: item}) + if item.Type == menu.CheckboxType { + item.Checked = !item.Checked + var checkState uint = win32.MF_UNCHECKED + if item.Checked { + checkState = win32.MF_CHECKED + } + for _, menuID := range p.checkboxItems[item] { + win32.CheckMenuItem(p.menu, int32(menuID), checkState) + } + // TODO: Check duplicate menu items + } + if item.Click != nil { + item.Click(&menu.CallbackData{MenuItem: item}) + } } } diff --git a/v2/internal/platform/win32/consts.go b/v2/internal/platform/win32/consts.go index 0c9435260..2617405fa 100644 --- a/v2/internal/platform/win32/consts.go +++ b/v2/internal/platform/win32/consts.go @@ -21,6 +21,7 @@ var ( procTrackPopupMenu = moduser32.NewProc("TrackPopupMenu") procDestroyMenu = moduser32.NewProc("DestroyMenu") procAppendMenuW = moduser32.NewProc("AppendMenuW") + procCheckMenuItem = moduser32.NewProc("CheckMenuItem") procCreateIconFromResourceEx = moduser32.NewProc("CreateIconFromResourceEx") procGetMessageW = moduser32.NewProc("GetMessageW") procIsDialogMessage = moduser32.NewProc("IsDialogMessageW") @@ -104,6 +105,7 @@ const ( MF_GRAYED = 0x00000001 MF_DISABLED = 0x00000002 MF_SEPARATOR = 0x00000800 + MF_UNCHECKED = 0x00000000 MF_CHECKED = 0x00000008 MF_POPUP = 0x00000010 MF_MENUBARBREAK = 0x00000020 diff --git a/v2/internal/platform/win32/menu.go b/v2/internal/platform/win32/menu.go index 9470fdda7..25a151dac 100644 --- a/v2/internal/platform/win32/menu.go +++ b/v2/internal/platform/win32/menu.go @@ -32,3 +32,12 @@ func AppendMenu(menu HMENU, flags uintptr, id uintptr, text string) bool { ) return ret != 0 } + +func CheckMenuItem(menu HMENU, id int32, flags uint) uint { + ret, _, _ := procCheckMenuItem.Call( + uintptr(menu), + uintptr(id), + uintptr(flags), + ) + return uint(ret) +} diff --git a/v2/pkg/menu/menuitem.go b/v2/pkg/menu/menuitem.go index ac5ebd7be..6ab928857 100644 --- a/v2/pkg/menu/menuitem.go +++ b/v2/pkg/menu/menuitem.go @@ -6,11 +6,6 @@ import ( "github.com/wailsapp/wails/v2/pkg/menu/keys" ) -type MenuItemImpl interface { - SetChecked(bool) - SetLabel(string) -} - // MenuItem represents a menuitem contained in a menu type MenuItem struct { // Label is what appears as the menu text @@ -58,9 +53,6 @@ type MenuItem struct { // Used for locking when removing elements removeLock sync.Mutex - - // Implementation of the runtime methods - Impl MenuItemImpl } // Parent returns the parent of the menu item. @@ -224,25 +216,54 @@ func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool { return true } -func (m *MenuItem) SetChecked(b bool) { - if m.Checked != b { - m.Checked = b - m.Impl.SetChecked(b) - } -} - func (m *MenuItem) SetLabel(name string) { if m.Label == name { return } m.Label = name - m.Impl.SetLabel(name) } func (m *MenuItem) IsSeparator() bool { return m.Type == SeparatorType } +func (m *MenuItem) IsCheckbox() bool { + return m.Type == CheckboxType +} + +func (m *MenuItem) Disable() *MenuItem { + m.Disabled = true + return m +} + +func (m *MenuItem) Enable() *MenuItem { + m.Disabled = false + return m +} + +func (m *MenuItem) OnClick(click Callback) *MenuItem { + m.Click = click + return m +} + +func (m *MenuItem) SetAccelerator(acc *keys.Accelerator) *MenuItem { + m.Accelerator = acc + return m +} + +func (m *MenuItem) SetChecked(value bool) *MenuItem { + m.Checked = value + m.Type = CheckboxType + return m +} + +func Label(label string) *MenuItem { + return &MenuItem{ + Type: TextType, + Label: label, + } +} + // Text is a helper to create basic Text menu items func Text(label string, accelerator *keys.Accelerator, click Callback) *MenuItem { return &MenuItem{