mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
[Windows] Handle menu accelerators
This commit is contained in:
parent
6c7cd651da
commit
d61bb9c79b
8 changed files with 109 additions and 37 deletions
|
|
@ -16,7 +16,7 @@ func main() {
|
|||
app := application.New(application.Options{
|
||||
Name: "Menu Demo",
|
||||
Description: "A demo of the menu system",
|
||||
//Assets: application.AlphaAssets,
|
||||
Assets: application.AlphaAssets,
|
||||
Mac: application.MacOptions{
|
||||
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||
},
|
||||
|
|
@ -35,7 +35,7 @@ func main() {
|
|||
myMenu.Add("Not Enabled").SetEnabled(false)
|
||||
|
||||
// Click callbacks
|
||||
myMenu.Add("Click Me!").SetBitmap(clickBitmap).OnClick(func(ctx *application.Context) {
|
||||
myMenu.Add("Click Me!").SetAccelerator("CmdOrCtrl+l").SetBitmap(clickBitmap).OnClick(func(ctx *application.Context) {
|
||||
switch ctx.ClickedMenuItem().Label() {
|
||||
case "Click Me!":
|
||||
ctx.ClickedMenuItem().SetLabel("Thanks mate!")
|
||||
|
|
|
|||
|
|
@ -311,7 +311,8 @@ type App struct {
|
|||
isDebugMode bool
|
||||
|
||||
// Keybindings
|
||||
keyBindings map[string]func(window *WebviewWindow)
|
||||
keyBindings map[string]func(window *WebviewWindow)
|
||||
keyBindingsLock sync.RWMutex
|
||||
|
||||
// Shutdown
|
||||
performingShutdown bool
|
||||
|
|
@ -863,6 +864,9 @@ func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow)
|
|||
return false
|
||||
}
|
||||
|
||||
a.keyBindingsLock.RLock()
|
||||
defer a.keyBindingsLock.RUnlock()
|
||||
|
||||
// Check key bindings
|
||||
callback, ok := a.keyBindings[acceleratorString]
|
||||
if !ok {
|
||||
|
|
@ -875,6 +879,18 @@ func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow)
|
|||
return true
|
||||
}
|
||||
|
||||
func (a *App) addKeyBinding(acceleratorString string, callback func(window *WebviewWindow)) {
|
||||
a.keyBindingsLock.Lock()
|
||||
defer a.keyBindingsLock.Unlock()
|
||||
a.keyBindings[acceleratorString] = callback
|
||||
}
|
||||
|
||||
func (a *App) removeKeyBinding(acceleratorString string) {
|
||||
a.keyBindingsLock.Lock()
|
||||
defer a.keyBindingsLock.Unlock()
|
||||
delete(a.keyBindings, acceleratorString)
|
||||
}
|
||||
|
||||
func (a *App) handleWindowKeyEvent(event *windowKeyEvent) {
|
||||
// Get window from window map
|
||||
a.windowsLock.RLock()
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ func (a *accelerator) String() string {
|
|||
}
|
||||
slices.Sort(result)
|
||||
if len(a.Key) > 0 {
|
||||
result = append(result, a.Key)
|
||||
result = append(result, strings.ToUpper(a.Key))
|
||||
}
|
||||
return strings.ToLower(strings.Join(result, "+"))
|
||||
return strings.Join(result, "+")
|
||||
}
|
||||
|
||||
var namedKeys = map[string]struct{}{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import (
|
|||
)
|
||||
|
||||
type windowsMenu struct {
|
||||
menu *Menu
|
||||
menu *Menu
|
||||
parentWindow *windowsWebviewWindow
|
||||
|
||||
hWnd w32.HWND
|
||||
hMenu w32.HMENU
|
||||
|
|
@ -36,6 +37,13 @@ func (w *windowsMenu) update() {
|
|||
func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
||||
for _, item := range inputMenu.items {
|
||||
if item.Hidden() {
|
||||
if item.accelerator != nil && item.callback != nil {
|
||||
if w.parentWindow != nil {
|
||||
w.parentWindow.parent.removeMenuBinding(item.accelerator)
|
||||
} else {
|
||||
globalApplication.removeKeyBinding(item.accelerator.String())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
w.currentMenuID++
|
||||
|
|
@ -52,21 +60,6 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
|||
if item.IsSeparator() {
|
||||
flags = flags | w32.MF_SEPARATOR
|
||||
}
|
||||
//
|
||||
//if item.IsCheckbox() {
|
||||
// w.checkboxItems[item] = append(w.checkboxItems[item], itemID)
|
||||
//}
|
||||
//if item.IsRadio() {
|
||||
// currentRadioGroup.Add(itemID, item)
|
||||
//} else {
|
||||
// if len(currentRadioGroup) > 0 {
|
||||
// for _, radioMember := range currentRadioGroup {
|
||||
// currentRadioGroup := currentRadioGroup
|
||||
// p.radioGroups[radioMember.MenuItem] = append(p.radioGroups[radioMember.MenuItem], ¤tRadioGroup)
|
||||
// }
|
||||
// currentRadioGroup = RadioGroup{}
|
||||
// }
|
||||
//}
|
||||
|
||||
if item.submenu != nil {
|
||||
flags = flags | w32.MF_POPUP
|
||||
|
|
@ -75,7 +68,18 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
|||
itemID = int(newSubmenu)
|
||||
}
|
||||
|
||||
var menuText = w32.MustStringToUTF16Ptr(item.Label())
|
||||
thisText := item.Label()
|
||||
if item.accelerator != nil && item.callback != nil {
|
||||
if w.parentWindow != nil {
|
||||
w.parentWindow.parent.addMenuBinding(item.accelerator, item)
|
||||
} else {
|
||||
globalApplication.addKeyBinding(item.accelerator.String(), func(w *WebviewWindow) {
|
||||
item.handleClick()
|
||||
})
|
||||
}
|
||||
thisText = thisText + "\t" + item.accelerator.String()
|
||||
}
|
||||
var menuText = w32.MustStringToUTF16Ptr(thisText)
|
||||
|
||||
w32.AppendMenu(parentMenu, flags, uintptr(itemID), menuText)
|
||||
if item.bitmap != nil {
|
||||
|
|
|
|||
|
|
@ -336,15 +336,12 @@ func (m *windowsMenuItem) getMenuInfo() *w32.MENUITEMINFO {
|
|||
mii.FType = w32.MFT_SEPARATOR
|
||||
} else {
|
||||
mii.FType = w32.MFT_STRING
|
||||
//var text string
|
||||
//if s := a.shortcut; s.Key != 0 {
|
||||
// text = fmt.Sprintf("%s\t%s", a.text, s.String())
|
||||
// shortcut2Action[a.shortcut] = a
|
||||
//} else {
|
||||
// text = a.text
|
||||
//}
|
||||
mii.DwTypeData = w32.MustStringToUTF16Ptr(m.label)
|
||||
mii.Cch = uint32(len([]rune(m.label)))
|
||||
thisText := m.label
|
||||
if m.menuItem.accelerator != nil {
|
||||
thisText += "\t" + m.menuItem.accelerator.String()
|
||||
}
|
||||
mii.DwTypeData = w32.MustStringToUTF16Ptr(thisText)
|
||||
mii.Cch = uint32(len([]rune(thisText)))
|
||||
}
|
||||
mii.WID = uint32(m.id)
|
||||
if m.Enabled() {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ func (r *RadioGroup) MenuID(item *MenuItem) int {
|
|||
type Win32Menu struct {
|
||||
isPopup bool
|
||||
menu w32.HMENU
|
||||
parentWindow *windowsWebviewWindow
|
||||
parent w32.HWND
|
||||
menuMapping map[int]*MenuItem
|
||||
checkboxItems map[*MenuItem][]int
|
||||
|
|
@ -61,6 +62,15 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
|||
var currentRadioGroup RadioGroup
|
||||
for _, item := range inputMenu.items {
|
||||
if item.Hidden() {
|
||||
if item.accelerator != nil {
|
||||
if p.parentWindow != nil {
|
||||
// Remove the accelerator from the keybindings
|
||||
p.parentWindow.parent.removeMenuBinding(item.accelerator)
|
||||
} else {
|
||||
// Remove the global keybindings
|
||||
globalApplication.removeKeyBinding(item.accelerator.String())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
p.currentMenuID++
|
||||
|
|
@ -85,7 +95,9 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
|||
p.checkboxItems[item] = append(p.checkboxItems[item], itemID)
|
||||
}
|
||||
if item.IsRadio() {
|
||||
currentRadioGroup.Add(itemID, item)
|
||||
if currentRadioGroup != nil {
|
||||
currentRadioGroup.Add(itemID, item)
|
||||
}
|
||||
} else {
|
||||
if len(currentRadioGroup) > 0 {
|
||||
for _, radioMember := range currentRadioGroup {
|
||||
|
|
@ -105,6 +117,18 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
|||
}
|
||||
|
||||
var menuText = item.Label()
|
||||
if item.accelerator != nil {
|
||||
menuText = menuText + "\t" + item.accelerator.String()
|
||||
if item.callback != nil {
|
||||
if p.parentWindow != nil {
|
||||
p.parentWindow.parent.addMenuBinding(item.accelerator, item)
|
||||
} else {
|
||||
globalApplication.addKeyBinding(item.accelerator.String(), func(w *WebviewWindow) {
|
||||
item.handleClick()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
ok := w32.AppendMenu(parentMenu, flags, uintptr(itemID), w32.MustStringToUTF16Ptr(menuText))
|
||||
if !ok {
|
||||
w32.Fatal(fmt.Sprintf("Error adding menu item: %s", menuText))
|
||||
|
|
@ -146,9 +170,10 @@ func NewPopupMenu(parent w32.HWND, inputMenu *Menu) *Win32Menu {
|
|||
result.Update()
|
||||
return result
|
||||
}
|
||||
func NewApplicationMenu(parent w32.HWND, inputMenu *Menu) *Win32Menu {
|
||||
func NewApplicationMenu(parent *windowsWebviewWindow, inputMenu *Menu) *Win32Menu {
|
||||
result := &Win32Menu{
|
||||
parent: parent,
|
||||
parentWindow: parent,
|
||||
parent: parent.hwnd,
|
||||
menuData: inputMenu,
|
||||
checkboxItems: make(map[*MenuItem][]int),
|
||||
radioGroups: make(map[*MenuItem][]*RadioGroup),
|
||||
|
|
|
|||
|
|
@ -132,7 +132,12 @@ type WebviewWindow struct {
|
|||
cancellers []func()
|
||||
|
||||
// keyBindings holds the keybindings for the window
|
||||
keyBindings map[string]func(window *WebviewWindow)
|
||||
keyBindings map[string]func(*WebviewWindow)
|
||||
keyBindingsLock sync.RWMutex
|
||||
|
||||
// menuBindings holds the menu bindings for the window
|
||||
menuBindings map[string]*MenuItem
|
||||
menuBindingsLock sync.RWMutex
|
||||
|
||||
// Indicates that the window is destroyed
|
||||
destroyed bool
|
||||
|
|
@ -211,6 +216,7 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow {
|
|||
eventListeners: make(map[uint][]*WindowEventListener),
|
||||
contextMenus: make(map[string]*Menu),
|
||||
eventHooks: make(map[uint][]*WindowEventListener),
|
||||
menuBindings: make(map[string]*MenuItem),
|
||||
}
|
||||
|
||||
result.setupEventMapping()
|
||||
|
|
@ -1176,12 +1182,23 @@ func (w *WebviewWindow) SetPosition(x int, y int) {
|
|||
}
|
||||
|
||||
func (w *WebviewWindow) processKeyBinding(acceleratorString string) bool {
|
||||
// Check menu bindings
|
||||
if w.menuBindings != nil {
|
||||
w.menuBindingsLock.RLock()
|
||||
defer w.menuBindingsLock.RUnlock()
|
||||
if menuItem := w.menuBindings[acceleratorString]; menuItem != nil {
|
||||
menuItem.handleClick()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Check key bindings
|
||||
if w.keyBindings != nil {
|
||||
w.keyBindingsLock.RLock()
|
||||
defer w.keyBindingsLock.RUnlock()
|
||||
if callback := w.keyBindings[acceleratorString]; callback != nil {
|
||||
// Execute callback
|
||||
go callback(w)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1203,3 +1220,15 @@ func (w *WebviewWindow) isDestroyed() bool {
|
|||
defer w.destroyedLock.RUnlock()
|
||||
return w.destroyed
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) removeMenuBinding(a *accelerator) {
|
||||
w.menuBindingsLock.Lock()
|
||||
defer w.menuBindingsLock.Unlock()
|
||||
w.menuBindings[a.String()] = nil
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) addMenuBinding(a *accelerator, menuItem *MenuItem) {
|
||||
w.menuBindingsLock.Lock()
|
||||
defer w.menuBindingsLock.Unlock()
|
||||
w.menuBindings[a.String()] = menuItem
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,7 +234,8 @@ func (w *windowsWebviewWindow) run() {
|
|||
theMenu = w.parent.options.Windows.Menu
|
||||
}
|
||||
if theMenu != nil {
|
||||
w.menu = NewApplicationMenu(w.hwnd, theMenu)
|
||||
w.menu = NewApplicationMenu(w, theMenu)
|
||||
w.menu.parentWindow = w
|
||||
appMenu = w.menu.menu
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue