[windows] Fixes for radiobox sync

This commit is contained in:
Lea Anthony
2021-07-10 17:03:46 +10:00
parent 856b81ab04
commit 4f2788a294
11 changed files with 212 additions and 17 deletions

View File

@@ -33,6 +33,7 @@ require (
github.com/tidwall/sjson v1.1.7
github.com/wzshiming/ctc v1.2.3
github.com/xyproto/xpm v1.2.1
github.com/ztrue/tracerr v0.3.0
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20210326060303-6b1517762897

View File

@@ -107,6 +107,8 @@ github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0 h1:FPGYnfxuuxqC
github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@@ -166,6 +168,8 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y=
github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@@ -14,7 +14,7 @@ extern void DisableWindowIcon(struct Application* app);
*/
import "C"
import (
"log"
"os"
"github.com/wailsapp/wails/v2/pkg/menu"
)
@@ -105,7 +105,9 @@ been sent.
func checkFatal(err error) {
if err != nil {
log.Fatal(err)
globalRadioGroupCache.Dump()
globalRadioGroupMap.Dump()
os.Exit(1)
}
}
@@ -150,7 +152,7 @@ func menuClicked(id uint32) {
// Determine if the menu is set or not
res, _, err := win32GetMenuState.Call(uintptr(menuItemDetails.parent), uintptr(id), uintptr(MF_BYCOMMAND))
if int(res) == -1 {
log.Fatal(err)
checkFatal(err)
}
flag := MF_CHECKED
@@ -163,11 +165,12 @@ func menuClicked(id uint32) {
menuItemDetails := getMenuCacheEntry(menuid)
res, _, err = win32CheckMenuItem.Call(uintptr(menuItemDetails.parent), uintptr(menuid), uintptr(flag))
if int(res) == -1 {
log.Fatal(err)
checkFatal(err)
}
}
case menu.RadioType:
selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID)
err := selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID)
checkFatal(err)
}
// Print the click error - it's not fatal

View File

@@ -3,8 +3,12 @@
package ffenestri
import (
"fmt"
"github.com/leaanthony/slicer"
"github.com/wailsapp/wails/v2/internal/menumanager"
"os"
"sync"
"text/tabwriter"
)
/* ---------------------------------------------------------------------------------
@@ -28,6 +32,26 @@ func NewCheckboxCache() *CheckboxCache {
}
}
func (c *CheckboxCache) Dump() {
// Start a new tabwriter
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
println("---------------- Checkbox", c, "Dump ----------------")
for _, processedMenu := range c.cache {
println("Menu", processedMenu)
for wailsMenuItemID, win32menus := range processedMenu {
println(" WailsMenu: ", wailsMenuItemID)
menus := slicer.String()
for _, win32menu := range win32menus {
menus.Add(fmt.Sprintf("%v", win32menu))
}
_, _ = fmt.Fprintf(w, "%s\t%s\n", wailsMenuItemID, menus.Join(", "))
_ = w.Flush()
}
}
}
func (c *CheckboxCache) addToCheckboxCache(menu *menumanager.ProcessedMenu, item wailsMenuItemID, menuID win32MenuItemID) {
// Get map for menu

View File

@@ -0,0 +1,28 @@
//+build windows,debug
package ffenestri
import (
"fmt"
"github.com/ztrue/tracerr"
"runtime"
"strings"
)
func wall(err error, inputs ...interface{}) error {
if err == nil {
return nil
}
pc, _, _, _ := runtime.Caller(1)
funcName := runtime.FuncForPC(pc).Name()
splitName := strings.Split(funcName, ".")
message := "[" + splitName[len(splitName)-1] + "]"
if len(inputs) > 0 {
params := []string{}
for _, param := range inputs {
params = append(params, fmt.Sprintf("%v", param))
}
message += "(" + strings.Join(params, " ") + ")"
}
return tracerr.Errorf(message)
}

View File

@@ -0,0 +1,47 @@
// +build windows,!debug
package ffenestri
import "C"
import (
"fmt"
"golang.org/x/sys/windows"
"log"
"os"
"runtime"
"strings"
"syscall"
)
func wall(err error, inputs ...interface{}) error {
if err == nil {
return nil
}
pc, _, _, _ := runtime.Caller(1)
funcName := runtime.FuncForPC(pc).Name()
splitName := strings.Split(funcName, ".")
message := "[" + splitName[len(splitName)-1] + "]"
if len(inputs) > 0 {
params := []string{}
for _, param := range inputs {
params = append(params, fmt.Sprintf("%v", param))
}
message += "(" + strings.Join(params, " ") + ")"
}
title, err := syscall.UTF16PtrFromString("Fatal Error")
if err != nil {
log.Fatal(err)
}
text, err := syscall.UTF16PtrFromString("There has been a fatal error. Details:\n" + message)
if err != nil {
log.Fatal(err)
}
var flags uint32 = windows.MB_ICONERROR | windows.MB_OK
_, err = windows.MessageBox(0, text, title, flags|windows.MB_SYSTEMMODAL)
os.Exit(1)
return err
}

View File

@@ -142,7 +142,10 @@ func (m *Menu) processRadioGroups() error {
for _, win32MenuID := range m.initiallySelectedRadioItems {
menuItemDetails := getMenuCacheEntry(win32MenuID)
wailsMenuID := wailsMenuItemID(menuItemDetails.item.ID)
selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID)
err := selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID)
if err != nil {
return err
}
}
return nil
@@ -156,6 +159,8 @@ func (m *Menu) Destroy() error {
// Unload this menu's radio groups from the cache
globalRadioGroupCache.removeMenuFromRadioBoxCache(m.wailsMenu.Menu)
globalRadioGroupMap.removeMenuFromRadioGroupMapping(m.wailsMenu.Menu)
// Delete menu
return destroyWin32Menu(m.menu)
}

View File

@@ -3,7 +3,11 @@
package ffenestri
import (
"fmt"
"github.com/leaanthony/slicer"
"os"
"sync"
"text/tabwriter"
"github.com/wailsapp/wails/v2/internal/menumanager"
)
@@ -36,6 +40,26 @@ func NewRadioGroupCache() *RadioGroupCache {
}
}
func (c *RadioGroupCache) Dump() {
// Start a new tabwriter
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
println("---------------- RadioGroupCache", c, "Dump ----------------")
for menu, processedMenu := range c.cache {
println("Menu", menu)
_, _ = fmt.Fprintf(w, "Wails ID \tWindows ID Pairs\n")
for wailsMenuItemID, radioGroupStartEnd := range processedMenu {
menus := slicer.String()
for _, se := range radioGroupStartEnd {
menus.Add(fmt.Sprintf("[%d -> %d]", se.startID, se.endID))
}
_, _ = fmt.Fprintf(w, "%s\t%s\n", wailsMenuItemID, menus.Join(", "))
_ = w.Flush()
}
}
}
func (c *RadioGroupCache) addToRadioGroupCache(menu *menumanager.ProcessedMenu, item wailsMenuItemID, radioGroupMaps []*radioGroupStartEnd) {
c.mutex.Lock()
@@ -87,6 +111,25 @@ func NewRadioGroupMap() *RadioGroupMap {
}
}
func (c *RadioGroupMap) Dump() {
// Start a new tabwriter
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
println("---------------- RadioGroupMap", c, "Dump ----------------")
for _, processedMenu := range c.cache {
_, _ = fmt.Fprintf(w, "Menu\tWails ID \tWindows IDs\n")
for wailsMenuItemID, win32menus := range processedMenu {
menus := slicer.String()
for _, win32menu := range win32menus {
menus.Add(fmt.Sprintf("%v", win32menu))
}
_, _ = fmt.Fprintf(w, "%p\t%s\t%s\n", processedMenu, wailsMenuItemID, menus.Join(", "))
_ = w.Flush()
}
}
}
func (m *RadioGroupMap) addRadioGroupMapping(menu *menumanager.ProcessedMenu, item wailsMenuItemID, win32ID win32MenuItemID) {
m.mutex.Lock()
@@ -106,6 +149,12 @@ func (m *RadioGroupMap) addRadioGroupMapping(menu *menumanager.ProcessedMenu, it
m.mutex.Unlock()
}
func (m *RadioGroupMap) removeMenuFromRadioGroupMapping(menu *menumanager.ProcessedMenu) {
m.mutex.Lock()
delete(m.cache, menu)
m.mutex.Unlock()
}
func (m *RadioGroupMap) getRadioGroupMapping(wailsMenuID wailsMenuItemID) []win32MenuItemID {
m.mutex.Lock()
result := []win32MenuItemID{}
@@ -119,7 +168,7 @@ func (m *RadioGroupMap) getRadioGroupMapping(wailsMenuID wailsMenuItemID) []win3
return result
}
func selectRadioItemFromWailsMenuID(wailsMenuID wailsMenuItemID, win32MenuID win32MenuItemID) {
func selectRadioItemFromWailsMenuID(wailsMenuID wailsMenuItemID, win32MenuID win32MenuItemID) error {
radioItemGroups := globalRadioGroupCache.getRadioGroupMappings(wailsMenuID)
// Figure out offset into group
var offset win32MenuItemID = 0
@@ -132,6 +181,14 @@ func selectRadioItemFromWailsMenuID(wailsMenuID wailsMenuItemID, win32MenuID win
for _, radioItemGroup := range radioItemGroups {
selectedMenuID := radioItemGroup.startID + offset
menuItemDetails := getMenuCacheEntry(selectedMenuID)
selectRadioItem(selectedMenuID, radioItemGroup.startID, radioItemGroup.endID, menuItemDetails.parent)
if menuItemDetails != nil {
if menuItemDetails.parent != 0 {
err := selectRadioItem(selectedMenuID, radioItemGroup.startID, radioItemGroup.endID, menuItemDetails.parent)
if err != nil {
return err
}
}
}
}
return nil
}

View File

@@ -3,7 +3,6 @@
package ffenestri
import (
"log"
"unsafe"
"github.com/wailsapp/wails/v2/internal/menumanager"
@@ -45,15 +44,15 @@ const MF_BYPOSITION uint32 = 0x00000400
func createWin32Menu() (win32Menu, error) {
res, _, err := win32CreateMenu.Call()
if res == 0 {
return 0, err
return 0, wall(err)
}
return win32Menu(res), nil
}
func destroyWin32Menu(menu win32Menu) error {
res, _, err := win32CreateMenu.Call(uintptr(menu))
res, _, err := win32DestroyMenu.Call(uintptr(menu))
if res == 0 {
return err
return wall(err, "Menu:", menu)
}
return nil
}
@@ -61,7 +60,7 @@ func destroyWin32Menu(menu win32Menu) error {
func createWin32PopupMenu() (win32Menu, error) {
res, _, err := win32CreatePopupMenu.Call()
if res == 0 {
return 0, err
return 0, wall(err)
}
return win32Menu(res), nil
}
@@ -78,7 +77,7 @@ func appendWin32MenuItem(menu win32Menu, flags uintptr, submenuOrID uintptr, lab
uintptr(unsafe.Pointer(menuText)),
)
if res == 0 {
return err
return wall(err, "Menu", menu, "Flags", flags, "submenuOrID", submenuOrID, "label", label)
}
return nil
}
@@ -86,14 +85,15 @@ func appendWin32MenuItem(menu win32Menu, flags uintptr, submenuOrID uintptr, lab
func setWindowMenu(window win32Window, menu win32Menu) error {
res, _, err := win32SetMenu.Call(uintptr(window), uintptr(menu))
if res == 0 {
return err
return wall(err, "window", window, "menu", menu)
}
return nil
}
func selectRadioItem(selectedMenuID, startMenuItemID, endMenuItemID win32MenuItemID, parent win32Menu) {
func selectRadioItem(selectedMenuID, startMenuItemID, endMenuItemID win32MenuItemID, parent win32Menu) error {
res, _, err := win32CheckMenuRadioItem.Call(uintptr(parent), uintptr(startMenuItemID), uintptr(endMenuItemID), uintptr(selectedMenuID), uintptr(MF_BYCOMMAND))
if int(res) == 0 {
log.Fatal(err)
return wall(err, selectedMenuID, startMenuItemID, endMenuItemID, parent)
}
return nil
}

View File

@@ -41,6 +41,7 @@ func (m *Manager) processApplicationMenu() error {
// Process the menu
m.processedApplicationMenu = NewWailsMenu(m.applicationMenuItemMap, m.applicationMenu)
m.processRadioGroups(m.processedApplicationMenu, m.applicationMenuItemMap)
applicationMenuJSON, err := m.processedApplicationMenu.AsJSON()
if err != nil {
return err

View File

@@ -22,6 +22,9 @@ type Manager struct {
// Tray menu stores
trayMenus map[string]*TrayMenu
trayMenuPointers map[*menu.TrayMenu]string
// Radio groups
radioGroups map[*menu.MenuItem][]*menu.MenuItem
}
func NewManager() *Manager {
@@ -31,6 +34,7 @@ func NewManager() *Manager {
contextMenuPointers: make(map[*menu.ContextMenu]string),
trayMenus: make(map[string]*TrayMenu),
trayMenuPointers: make(map[*menu.TrayMenu]string),
radioGroups: make(map[*menu.MenuItem][]*menu.MenuItem),
}
}
@@ -73,6 +77,14 @@ func (m *Manager) ProcessClick(menuID string, data string, menuType string, pare
menuItem.Checked = !menuItem.Checked
}
if menuItem.Type == menu.RadioType {
println("Toggle radio")
// Get my radio group
for _, radioMenuItem := range m.radioGroups[menuItem] {
radioMenuItem.Checked = (radioMenuItem == menuItem)
}
}
if menuItem.Click == nil {
// No callback
return fmt.Errorf("No callback for menu '%s'", menuItem.Label)
@@ -89,3 +101,16 @@ func (m *Manager) ProcessClick(menuID string, data string, menuType string, pare
return nil
}
func (m *Manager) processRadioGroups(processedMenu *WailsMenu, itemMap *MenuItemMap) {
for _, group := range processedMenu.RadioGroups {
radioGroupMenuItems := []*menu.MenuItem{}
for _, member := range group.Members {
item := m.getMenuItemByID(itemMap, member)
radioGroupMenuItems = append(radioGroupMenuItems, item)
}
for _, radioGroupMenuItem := range radioGroupMenuItems {
m.radioGroups[radioGroupMenuItem] = radioGroupMenuItems
}
}
}