5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 02:19:43 +08:00
wails/v2/internal/ffenestri/windows_menu.go
Eng Zer Jun ae341be638
chore: run go fmt ./... and go mod tidy
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2021-11-25 12:14:27 +08:00

234 lines
5.9 KiB
Go

//go:build windows
// +build windows
package ffenestri
import (
"fmt"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/menu/keys"
"runtime"
"strings"
)
//-------------------- Types ------------------------
type win32MenuItemID uint32
type win32Menu uintptr
type win32Window uintptr
type wailsMenuItemID string // The internal menu ID
type Menu struct {
wailsMenu *menumanager.WailsMenu
menu win32Menu
menuType menuType
// A list of all checkbox and radio menuitems we
// create for this menu
checkboxes []win32MenuItemID
radioboxes []win32MenuItemID
initiallySelectedRadioItems []win32MenuItemID
}
func createMenu(wailsMenu *menumanager.WailsMenu, menuType menuType) (*Menu, error) {
mainMenu, err := createWin32Menu()
if err != nil {
return nil, err
}
result := &Menu{
wailsMenu: wailsMenu,
menu: mainMenu,
menuType: menuType,
}
// Process top level menus
for _, toplevelmenu := range applicationMenu.Menu.Items {
err := result.processMenuItem(result.menu, toplevelmenu)
if err != nil {
return nil, err
}
}
err = result.processRadioGroups()
if err != nil {
return nil, err
}
return result, nil
}
func (m *Menu) processMenuItem(parent win32Menu, menuItem *menumanager.ProcessedMenuItem) error {
// Ignore hidden items
if menuItem.Hidden {
return nil
}
// Calculate the flags for this menu item
flags := uintptr(calculateFlags(menuItem))
switch menuItem.Type {
case menu.SubmenuType:
submenu, err := createWin32PopupMenu()
if err != nil {
return err
}
for _, submenuItem := range menuItem.SubMenu.Items {
err = m.processMenuItem(submenu, submenuItem)
if err != nil {
return err
}
}
err = appendWin32MenuItem(parent, flags, uintptr(submenu), menuItem.Label)
if err != nil {
return err
}
case menu.TextType, menu.CheckboxType, menu.RadioType:
win32ID := addMenuCacheEntry(parent, m.menuType, menuItem, m.wailsMenu.Menu)
if menuItem.Accelerator != nil {
m.processAccelerator(menuItem)
}
label := menuItem.Label
//label := fmt.Sprintf("%s (%d)", menuItem.Label, win32ID)
err := appendWin32MenuItem(parent, flags, uintptr(win32ID), label)
if err != nil {
return err
}
if menuItem.Type == menu.CheckboxType {
// We need to maintain a list of this menu's checkboxes
m.checkboxes = append(m.checkboxes, win32ID)
globalCheckboxCache.addToCheckboxCache(m.wailsMenu.Menu, wailsMenuItemID(menuItem.ID), win32ID)
}
if menuItem.Type == menu.RadioType {
// We need to maintain a list of this menu's radioitems
m.radioboxes = append(m.radioboxes, win32ID)
globalRadioGroupMap.addRadioGroupMapping(m.wailsMenu.Menu, wailsMenuItemID(menuItem.ID), win32ID)
if menuItem.Checked {
m.initiallySelectedRadioItems = append(m.initiallySelectedRadioItems, win32ID)
}
}
case menu.SeparatorType:
err := appendWin32MenuItem(parent, flags, 0, "")
if err != nil {
return err
}
}
return nil
}
func (m *Menu) processRadioGroups() error {
for _, rg := range applicationMenu.RadioGroups {
startWailsMenuID := wailsMenuItemID(rg.Members[0])
endWailsMenuID := wailsMenuItemID(rg.Members[len(rg.Members)-1])
startIDs := globalRadioGroupMap.getRadioGroupMapping(startWailsMenuID)
endIDs := globalRadioGroupMap.getRadioGroupMapping(endWailsMenuID)
var radioGroupMaps = []*radioGroupStartEnd{}
for index := range startIDs {
startID := startIDs[index]
endID := endIDs[index]
thisRadioGroup := &radioGroupStartEnd{
startID: startID,
endID: endID,
}
radioGroupMaps = append(radioGroupMaps, thisRadioGroup)
}
// Set this for each member
for _, member := range rg.Members {
id := wailsMenuItemID(member)
globalRadioGroupCache.addToRadioGroupCache(m.wailsMenu.Menu, id, radioGroupMaps)
}
}
// Enable all initially checked radio items
for _, win32MenuID := range m.initiallySelectedRadioItems {
menuItemDetails := getMenuCacheEntry(win32MenuID)
wailsMenuID := wailsMenuItemID(menuItemDetails.item.ID)
err := selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID)
if err != nil {
return err
}
}
return nil
}
func (m *Menu) Destroy() error {
// Release the MenuIDs
releaseMenuIDsForProcessedMenu(m.wailsMenu.Menu)
// Unload this menu's checkboxes from the cache
globalCheckboxCache.removeMenuFromCheckboxCache(m.wailsMenu.Menu)
// Unload this menu's radio groups from the cache
globalRadioGroupCache.removeMenuFromRadioBoxCache(m.wailsMenu.Menu)
globalRadioGroupMap.removeMenuFromRadioGroupMapping(m.wailsMenu.Menu)
// Free up callbacks
resetCallbacks()
// Delete menu
return destroyWin32Menu(m.menu)
}
func (m *Menu) processAccelerator(menuitem *menumanager.ProcessedMenuItem) {
// Add in shortcut to label if there is no "\t" override
if !strings.Contains(menuitem.Label, "\t") {
menuitem.Label += "\t" + keys.Stringify(menuitem.Accelerator, runtime.GOOS)
}
// Calculate the modifier
var modifiers uint8
for _, mod := range menuitem.Accelerator.Modifiers {
switch mod {
case keys.ControlKey, keys.CmdOrCtrlKey:
modifiers |= 1
case keys.OptionOrAltKey:
modifiers |= 2
case keys.ShiftKey:
modifiers |= 4
//case keys.SuperKey:
// modifiers |= 8
}
}
var keycode = calculateKeycode(strings.ToLower(menuitem.Accelerator.Key))
if keycode == 0 {
fmt.Printf("WARNING: Key '%s' is unsupported in windows. Cannot bind callback.", menuitem.Accelerator.Key)
return
}
addMenuCallback(keycode, modifiers, menuitem.ID, m.menuType)
}
var flagMap = map[menu.Type]uint32{
menu.TextType: MF_STRING,
menu.SeparatorType: MF_SEPARATOR,
menu.SubmenuType: MF_STRING | MF_POPUP,
menu.CheckboxType: MF_STRING,
menu.RadioType: MF_STRING,
}
func calculateFlags(menuItem *menumanager.ProcessedMenuItem) uint32 {
result := flagMap[menuItem.Type]
if menuItem.Disabled {
result |= MF_DISABLED
}
if menuItem.Type == menu.CheckboxType && menuItem.Checked {
result |= MF_CHECKED
}
return result
}