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:
parent
1bafc6f22f
commit
0dc7b3c549
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user