5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 03:21:32 +08:00

More control over menus (#4031)

* Add prepend and clear method to menus

* Document appending and clearing menus

* Add `Destroy()`
Add notes to documentation.

* Remove menu item from map when destroying

* Remove menu items from map when clearing

* Update v3/pkg/application/menu.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Minor updates

* Fix build error

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Falco Gerritsjans 2025-02-16 02:08:08 +01:00 committed by GitHub
parent 1bafc6f22f
commit 0dc7b3c549
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 144 additions and 0 deletions

View File

@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New Menu guide by [@leaanthony](https://github.com/leaanthony)
- Add doc comments for Service API by [@fbbdev](https://github.com/fbbdev) in [#4024](https://github.com/wailsapp/wails/pull/4024)
- Add function `application.NewServiceWithOptions` to initialise services with additional configuration by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024)
- Improved menu control by [@FalcoG](https://github.com/FalcoG) and [@leaanthony](https://github.com/leaanthony) in [#4031](https://github.com/wailsapp/wails/pull/4031)
- More documentation by [@leaanthony](https://github.com/leaanthony)
- Support cancellation of events in standard event listeners by [@leaanthony](https://github.com/leaanthony)
- Systray `Hide`, `Show` and `Destroy` support by [@leaanthony](https://github.com/leaanthony)

View File

@ -56,6 +56,65 @@ submenu.Add("Open")
submenu.Add("Save")
```
#### Combining menus
A menu can be added into another menu by appending or prepending it.
```go
menu := application.NewMenu()
menu.Add("First Menu")
secondaryMenu := application.NewMenu()
secondaryMenu.Add("Second Menu")
// insert 'secondaryMenu' after 'menu'
menu.Append(secondaryMenu)
// insert 'secondaryMenu' before 'menu'
menu.Prepend(secondaryMenu)
// update the menu
menu.Update()
```
:::note
By default, `prepend` and `append` will share state with the original menu. If you want to create a new menu with its own state,
you can call `.Clone()` on the menu.
E.g: `menu.Append(secondaryMenu.Clone())`
:::
#### Clearing a menu
In some cases it'll be better to construct a whole new menu if you are working with a variable amount of menu items.
This will clear all items on an existing menu and allows you to add items again.
```go
menu := application.NewMenu()
menu.Add("Waiting for update...")
// after certain logic, the menu has to be updated
menu.Clear()
menu.Add("Update complete!")
menu.Update()
```
:::note
Clearing a menu simply clears the menu items at the top level. Whilst Submenus won't be visible, they will still occupy memory
so be sure to manage your menus carefully.
:::
#### Destroying a menu
If you want to clear and release a menu, use the `Destroy()` method:
```go
menu := application.NewMenu()
menu.Add("Waiting for update...")
// after certain logic, the menu has to be destroyed
menu.Destroy()
```
### Menu Item Properties
Menu items have several properties that can be configured:

View File

@ -587,6 +587,10 @@ func menuItemNew(label string, bitmap []byte) pointer {
return menuItemAddProperties(C.gtk_menu_item_new(), label, bitmap)
}
func menuItemDestroy(widget pointer) {
C.gtk_widget_destroy((*C.GtkWidget)(widget))
}
func menuItemAddProperties(menuItem *C.GtkWidget, label string, bitmap []byte) pointer {
/*
// FIXME: Support accelerator configuration

View File

@ -69,6 +69,21 @@ func (m *Menu) Update() {
m.impl.update()
}
// Clear all menu items
func (m *Menu) Clear() {
for _, item := range m.items {
removeMenuItemByID(item.id)
}
m.items = nil
}
func (m *Menu) Destroy() {
for _, item := range m.items {
item.Destroy()
}
m.items = nil
}
func (m *Menu) AddSubmenu(s string) *Menu {
result := NewSubMenuItem(s)
m.items = append(m.items, result)
@ -186,10 +201,19 @@ func (m *Menu) Clone() *Menu {
return result
}
// Append menu to an existing menu
func (m *Menu) Append(in *Menu) {
if in == nil {
return
}
m.items = append(m.items, in.items...)
}
// Prepend menu before an existing menu
func (m *Menu) Prepend(in *Menu) {
m.items = append(in.items, m.items...)
}
func (a *App) NewMenu() *Menu {
return &Menu{}
}

View File

@ -33,6 +33,12 @@ func getMenuItemByID(id uint) *MenuItem {
return menuItemMap[id]
}
func removeMenuItemByID(id uint) {
menuItemMapLock.Lock()
defer menuItemMapLock.Unlock()
delete(menuItemMap, id)
}
type menuItemImpl interface {
setTooltip(s string)
setLabel(s string)
@ -41,6 +47,7 @@ type menuItemImpl interface {
setAccelerator(accelerator *accelerator)
setHidden(hidden bool)
setBitmap(bitmap []byte)
destroy()
}
type MenuItem struct {
@ -425,3 +432,29 @@ func (m *MenuItem) Clone() *MenuItem {
}
return result
}
func (m *MenuItem) Destroy() {
removeMenuItemByID(m.id)
// Clean up resources
if m.impl != nil {
m.impl.destroy()
}
if m.submenu != nil {
m.submenu.Destroy()
m.submenu = nil
}
if m.contextMenuData != nil {
m.contextMenuData = nil
}
if m.accelerator != nil {
m.accelerator = nil
}
m.callback = nil
m.radioGroupMembers = nil
}

View File

@ -295,6 +295,11 @@ void setMenuItemBitmap(void* nsMenuItem, unsigned char *bitmap, int length) {
NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:bitmap length:length]];
[menuItem setImage:image];
}
void destroyMenuItem(void* nsMenuItem) {
MenuItem *menuItem = (MenuItem *)nsMenuItem;
[menuItem release];
}
*/
import "C"
import (
@ -344,6 +349,10 @@ func (m macosMenuItem) setAccelerator(accelerator *accelerator) {
C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier)
}
func (m macosMenuItem) destroy() {
C.destroyMenuItem(m.nsMenuItem)
}
func newMenuItemImpl(item *MenuItem) *macosMenuItem {
result := &macosMenuItem{
menuItem: item,

View File

@ -21,6 +21,14 @@ func (l linuxMenuItem) setTooltip(tooltip string) {
})
}
func (l linuxMenuItem) destroy() {
InvokeSync(func() {
l.blockSignal()
defer l.unBlockSignal()
menuItemDestroy(l.native)
})
}
func (l linuxMenuItem) blockSignal() {
if l.handlerId != 0 {
menuItemSignalBlock(l.native, l.handlerId, true)

View File

@ -96,6 +96,10 @@ func (m *windowsMenuItem) setChecked(checked bool) {
m.update()
}
func (m *windowsMenuItem) destroy() {
w32.RemoveMenu(m.hMenu, m.id, w32.MF_BYCOMMAND)
}
func (m *windowsMenuItem) setAccelerator(accelerator *accelerator) {
//// Set the keyboard shortcut of the menu item
//var modifier C.int

View File

@ -87,6 +87,8 @@ func (s *systrayMenuItem) setDisabled(disabled bool) {
}
}
func (s *systrayMenuItem) destroy() {}
func (s *systrayMenuItem) setChecked(checked bool) {
v := dbus.MakeVariant(0)
if checked {