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

[WIP] New menu processor

This commit is contained in:
Lea Anthony 2021-01-10 21:31:13 +11:00
parent 3a2d01813a
commit 8ee8c9b07c
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
12 changed files with 431 additions and 182 deletions

View File

@ -6,6 +6,7 @@ import (
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/ffenestri"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
@ -33,6 +34,8 @@ type App struct {
contextmenus *subsystem.ContextMenus
dispatcher *messagedispatcher.Dispatcher
menuManager *menumanager.Manager
// Indicates if the app is in debug mode
debug bool
@ -54,13 +57,18 @@ func CreateApp(appoptions *options.App) (*App, error) {
myLogger := logger.New(appoptions.Logger)
myLogger.SetLogLevel(appoptions.LogLevel)
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger)
// Create the menu manager
menuManager := menumanager.NewManager()
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
result := &App{
window: window,
servicebus: servicebus.New(myLogger),
logger: myLogger,
bindings: binding.NewBindings(myLogger),
window: window,
servicebus: servicebus.New(myLogger),
logger: myLogger,
bindings: binding.NewBindings(myLogger),
menuManager: menuManager,
}
result.options = appoptions
@ -157,7 +165,7 @@ func (a *App) Run() error {
// Optionally start the menu subsystem
if applicationMenu != nil {
menusubsystem, err := subsystem.NewMenu(applicationMenu, a.servicebus, a.logger)
menusubsystem, err := subsystem.NewMenu(a.servicebus, a.logger, a.menuManager)
if err != nil {
return err
}

View File

@ -2,6 +2,7 @@ package ffenestri
import (
"encoding/json"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/pkg/menu"
"runtime"
"strings"
@ -35,6 +36,9 @@ type Application struct {
// This is the main app pointer
app unsafe.Pointer
// Manages menus
menuManager *menumanager.Manager
// Logger
logger logger.CustomLogger
}
@ -54,10 +58,11 @@ func init() {
}
// NewApplicationWithConfig creates a new application based on the given config
func NewApplicationWithConfig(config *options.App, logger *logger.Logger) *Application {
func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuManager *menumanager.Manager) *Application {
return &Application{
config: config,
logger: logger.CustomLogger("Ffenestri"),
config: config,
logger: logger.CustomLogger("Ffenestri"),
menuManager: menuManager,
}
}

View File

@ -76,23 +76,10 @@ func (a *Application) processPlatformSettings() error {
}
// Process menu
applicationMenu := options.GetApplicationMenu(a.config)
if applicationMenu != nil {
/*
As radio groups need to be manually managed on OSX,
we preprocess the menu to determine the radio groups.
This is defined as any adjacent menu item of type "RadioType".
We keep a record of every radio group member we discover by saving
a list of all members of the group and the number of members
in the group (this last one is for optimisation at the C layer).
*/
processedMenu := NewProcessedMenu(applicationMenu)
applicationMenuJSON, err := json.Marshal(processedMenu)
if err != nil {
return err
}
C.SetMenu(a.app, a.string2CString(string(applicationMenuJSON)))
//applicationMenu := options.GetApplicationMenu(a.config)
applicationMenu := a.menuManager.GetApplicationMenuJSON()
if applicationMenu != "" {
C.SetMenu(a.app, a.string2CString(applicationMenu))
}
// Process tray

View File

@ -0,0 +1,54 @@
package menumanager
import (
"fmt"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// MenuItemMap holds a mapping between menuIDs and menu items
type MenuItemMap struct {
idToMenuItemMap map[string]*menu.MenuItem
menuItemToIDMap map[*menu.MenuItem]string
}
func NewMenuItemMap() *MenuItemMap {
result := &MenuItemMap{
idToMenuItemMap: make(map[string]*menu.MenuItem),
menuItemToIDMap: make(map[*menu.MenuItem]string),
}
return result
}
func (m *MenuItemMap) AddMenu(menu *menu.Menu) {
for _, item := range menu.Items {
m.processMenuItem(item)
}
}
func (m *MenuItemMap) Dump() {
println("idToMenuItemMap:")
for key, value := range m.idToMenuItemMap {
fmt.Printf(" %s\t%p\n", key, value)
}
println("\nmenuItemToIDMap")
for key, value := range m.menuItemToIDMap {
fmt.Printf(" %p\t%s\n", key, value)
}
}
func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) {
if item.SubMenu != nil {
for _, submenuitem := range item.SubMenu.Items {
m.processMenuItem(submenuitem)
}
}
// Create a unique ID for this menu item
menuID := fmt.Sprintf("%d", len(m.idToMenuItemMap))
// Store references
m.idToMenuItemMap[menuID] = item
m.menuItemToIDMap[item] = menuID
}

View File

@ -0,0 +1,82 @@
package menumanager
import (
"fmt"
"github.com/wailsapp/wails/v2/pkg/menu"
)
type Manager struct {
// The application menu.
applicationMenu *menu.Menu
applicationMenuJSON string
// Our menu mappings
menuItemMap *MenuItemMap
}
func NewManager() *Manager {
return &Manager{
menuItemMap: NewMenuItemMap(),
}
}
func (m *Manager) SetApplicationMenu(applicationMenu *menu.Menu) error {
if applicationMenu == nil {
return nil
}
m.applicationMenu = applicationMenu
m.menuItemMap.AddMenu(applicationMenu)
return m.processApplicationMenu()
}
func (m *Manager) GetApplicationMenuJSON() string {
return m.applicationMenuJSON
}
func (m *Manager) processApplicationMenu() error {
// Process the menu
processedApplicationMenu := m.NewWailsMenu(m.applicationMenu)
applicationMenuJSON, err := processedApplicationMenu.AsJSON()
if err != nil {
return err
}
m.applicationMenuJSON = applicationMenuJSON
return nil
}
func (m *Manager) getMenuItemByID(menuId string) *menu.MenuItem {
return m.menuItemMap.idToMenuItemMap[menuId]
}
func (m *Manager) ProcessClick(menuID string) error {
// Get the menu item
menuItem := m.getMenuItemByID(menuID)
if menuItem == nil {
return fmt.Errorf("Cannot process menuid %s - unknown", menuID)
}
// Is the menu item a checkbox?
if menuItem.Type == menu.CheckboxType {
// Toggle state
menuItem.Checked = !menuItem.Checked
}
if menuItem.Click == nil {
// No callback
return fmt.Errorf("No callback for menu '%s'", menuItem.Label)
}
// Create new Callback struct
callbackData := &menu.CallbackData{
MenuItem: menuItem,
ContextData: "",
}
// Call back!
go menuItem.Click(callbackData)
return nil
}

View File

@ -0,0 +1,156 @@
package menumanager
import (
"encoding/json"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/menu/keys"
)
type ProcessedMenuItem struct {
ID string
// Label is what appears as the menu text
Label string
// Role is a predefined menu type
Role menu.Role `json:"Role,omitempty"`
// Accelerator holds a representation of a key binding
Accelerator *keys.Accelerator `json:"Accelerator,omitempty"`
// Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu
Type menu.Type
// Disabled makes the item unselectable
Disabled bool
// Hidden ensures that the item is not shown in the menu
Hidden bool
// Checked indicates if the item is selected (used by Checkbox and Radio types only)
Checked bool
// Submenu contains a list of menu items that will be shown as a submenu
//SubMenu []*MenuItem `json:"SubMenu,omitempty"`
SubMenu *ProcessedMenu `json:"SubMenu,omitempty"`
// Foreground colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red
Foreground int
// Background colour
Background int
}
func (m *Manager) NewProcessedMenuItem(menuItem *menu.MenuItem) *ProcessedMenuItem {
ID := m.menuItemMap.menuItemToIDMap[menuItem]
result := &ProcessedMenuItem{
ID: ID,
Label: menuItem.Label,
Role: menuItem.Role,
Accelerator: menuItem.Accelerator,
Type: menuItem.Type,
Disabled: menuItem.Disabled,
Hidden: menuItem.Hidden,
Checked: menuItem.Checked,
Foreground: menuItem.Foreground,
Background: menuItem.Background,
}
if menuItem.SubMenu != nil {
result.SubMenu = m.NewProcessedMenu(menuItem.SubMenu)
}
return result
}
type ProcessedMenu struct {
Items []*ProcessedMenuItem
}
func (m *Manager) NewProcessedMenu(menu *menu.Menu) *ProcessedMenu {
result := &ProcessedMenu{}
for _, item := range menu.Items {
processedMenuItem := m.NewProcessedMenuItem(item)
result.Items = append(result.Items, processedMenuItem)
}
return result
}
// WailsMenu is the original menu with the addition
// of radio groups extracted from the menu data
type WailsMenu struct {
Menu *ProcessedMenu
RadioGroups []*RadioGroup
currentRadioGroup []string
}
// RadioGroup holds all the members of the same radio group
type RadioGroup struct {
Members []string
Length int
}
func (m *Manager) NewWailsMenu(menu *menu.Menu) *WailsMenu {
result := &WailsMenu{}
// Process the menus
result.Menu = m.NewProcessedMenu(menu)
// Process the radio groups
result.processRadioGroups()
return result
}
func (w *WailsMenu) AsJSON() (string, error) {
menuAsJSON, err := json.Marshal(w)
if err != nil {
return "", err
}
return string(menuAsJSON), nil
}
func (w *WailsMenu) processRadioGroups() {
// Loop over top level menus
for _, item := range w.Menu.Items {
// Process MenuItem
w.processMenuItem(item)
}
w.finaliseRadioGroup()
}
func (w *WailsMenu) processMenuItem(item *ProcessedMenuItem) {
switch item.Type {
// We need to recurse submenus
case menu.SubmenuType:
// Finalise any current radio groups as they don't trickle down to submenus
w.finaliseRadioGroup()
// Process each submenu item
for _, subitem := range item.SubMenu.Items {
w.processMenuItem(subitem)
}
case menu.RadioType:
// Add the item to the radio group
w.currentRadioGroup = append(w.currentRadioGroup, item.ID)
default:
w.finaliseRadioGroup()
}
}
func (w *WailsMenu) finaliseRadioGroup() {
// If we were processing a radio group, fix up the references
if len(w.currentRadioGroup) > 0 {
// Create new radiogroup
group := &RadioGroup{
Members: w.currentRadioGroup,
Length: len(w.currentRadioGroup),
}
w.RadioGroups = append(w.RadioGroups, group)
// Empty the radio group
w.currentRadioGroup = []string{}
}
}

View File

@ -1,6 +1,7 @@
package subsystem
import (
"github.com/wailsapp/wails/v2/internal/menumanager"
"strings"
"sync"
@ -35,15 +36,15 @@ type Menu struct {
// logger
logger logger.CustomLogger
// The application menu
applicationMenu *menu.Menu
// Service Bus
bus *servicebus.ServiceBus
// Menu Manager
menuManager *menumanager.Manager
}
// NewMenu creates a new menu subsystem
func NewMenu(applicationMenu *menu.Menu, bus *servicebus.ServiceBus, logger *logger.Logger) (*Menu, error) {
func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
// Register quit channel
quitChannel, err := bus.Subscribe("quit")
@ -58,18 +59,15 @@ func NewMenu(applicationMenu *menu.Menu, bus *servicebus.ServiceBus, logger *log
}
result := &Menu{
quitChannel: quitChannel,
menuChannel: menuChannel,
logger: logger.CustomLogger("Menu Subsystem"),
listeners: make(map[string][]func(*menu.MenuItem)),
menuItems: make(map[string]*menu.MenuItem),
applicationMenu: applicationMenu,
bus: bus,
quitChannel: quitChannel,
menuChannel: menuChannel,
logger: logger.CustomLogger("Menu Subsystem"),
listeners: make(map[string][]func(*menu.MenuItem)),
menuItems: make(map[string]*menu.MenuItem),
bus: bus,
menuManager: menuManager,
}
// Build up list of item/id pairs
result.processMenu(applicationMenu)
return result, nil
}
@ -99,21 +97,11 @@ func (m *Menu) Start() error {
m.logger.Trace("Got Menu clicked Message: %s %+v", menuMessage.Topic(), menuMessage.Data())
menuid := menuMessage.Data().(string)
// Get the menu item
menuItem := m.menuItems[menuid]
if menuItem == nil {
m.logger.Trace("Cannot process menuid %s - unknown", menuid)
return
err := m.menuManager.ProcessClick(menuid)
if err != nil {
m.logger.Trace("%s", err.Error())
}
// Is the menu item a checkbox?
if menuItem.Type == menu.CheckboxType {
// Toggle state
menuItem.Checked = !menuItem.Checked
}
// Notify listeners
m.notifyListeners(menuid, menuItem)
case "on":
listenerDetails := menuMessage.Data().(*message.MenuOnMessage)
id := listenerDetails.MenuID
@ -121,11 +109,11 @@ func (m *Menu) Start() error {
// Make sure we catch any menu updates
case "update":
updatedMenu := menuMessage.Data().(*menu.Menu)
m.processMenu(updatedMenu)
// Notify frontend of menu change
m.bus.Publish("menufrontend:update", updatedMenu)
//updatedMenu := menuMessage.Data().(*menu.Menu)
//m.processMenu(updatedMenu)
//
//// Notify frontend of menu change
//m.bus.Publish("menufrontend:update", updatedMenu)
default:
m.logger.Error("unknown menu message: %+v", menuMessage)
@ -140,56 +128,6 @@ func (m *Menu) Start() error {
return nil
}
func (m *Menu) processMenu(applicationMenu *menu.Menu) {
// Initialise the variables
m.menuItems = make(map[string]*menu.MenuItem)
m.applicationMenu = applicationMenu
for _, item := range applicationMenu.Items {
m.processMenuItem(item)
}
}
func (m *Menu) processMenuItem(item *menu.MenuItem) {
if item.SubMenu != nil {
for _, submenuitem := range item.SubMenu.Items {
m.processMenuItem(submenuitem)
}
return
}
if item.ID != "" {
if m.menuItems[item.ID] != nil {
m.logger.Error("Menu id '%s' is used by multiple menu items: %s %s", m.menuItems[item.ID].Label, item.Label)
return
}
m.menuItems[item.ID] = item
}
}
// Notifies listeners that the given menu was clicked
func (m *Menu) notifyListeners(menuid string, menuItem *menu.MenuItem) {
// Get list of menu listeners
listeners := m.listeners[menuid]
if listeners == nil {
m.logger.Trace("No listeners for MenuItem with ID '%s'", menuid)
return
}
// Lock the listeners
m.notifyLock.Lock()
// Callback in goroutine
for _, listener := range listeners {
go listener(menuItem)
}
// Unlock
m.notifyLock.Unlock()
}
func (m *Menu) shutdown() {
m.logger.Trace("Shutdown")
}

8
v2/pkg/menu/callback.go Normal file
View File

@ -0,0 +1,8 @@
package menu
type CallbackData struct {
MenuItem *MenuItem
ContextData string
}
type Callback func(*CallbackData)

View File

@ -24,6 +24,9 @@ type MenuItem struct {
//SubMenu []*MenuItem `json:"SubMenu,omitempty"`
SubMenu *Menu `json:"SubMenu,omitempty"`
// Callback function when menu clicked
Click Callback `json:"-"`
// Foreground colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red
Foreground int
@ -226,12 +229,13 @@ func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool {
}
// Text is a helper to create basic Text menu items
func Text(label string, id string, accelerator *keys.Accelerator) *MenuItem {
func Text(label string, id string, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: TextType,
Accelerator: accelerator,
Click: click,
}
}
@ -243,24 +247,26 @@ func Separator() *MenuItem {
}
// Radio is a helper to create basic Radio menu items with an accelerator
func Radio(label string, id string, selected bool, accelerator *keys.Accelerator) *MenuItem {
func Radio(label string, id string, selected bool, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: RadioType,
Checked: selected,
Accelerator: accelerator,
Click: click,
}
}
// Checkbox is a helper to create basic Checkbox menu items
func Checkbox(label string, id string, checked bool, accelerator *keys.Accelerator) *MenuItem {
func Checkbox(label string, id string, checked bool, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: CheckboxType,
Checked: checked,
Accelerator: accelerator,
Click: click,
}
}

View File

@ -3,7 +3,6 @@ package main
import (
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/logger"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/mac"
"log"
@ -11,6 +10,8 @@ import (
func main() {
Menu := &Menu{}
// Create application with options
app, err := wails.CreateAppWithOptions(&options.App{
Title: "Kitchen Sink",
@ -21,17 +22,17 @@ func main() {
//Tray: menu.NewMenuFromItems(menu.AppMenu()),
//Menu: menu.NewMenuFromItems(menu.AppMenu()),
//StartHidden: true,
ContextMenus: createContextMenus(),
//ContextMenus: createContextMenus(),
Mac: &mac.Options{
WebviewIsTransparent: true,
WindowBackgroundIsTranslucent: true,
// Comment out line below to see Window.SetTitle() work
TitleBar: mac.TitleBarHiddenInset(),
Menu: createApplicationMenu(),
Tray: &menu.Tray{
Icon: "light",
Menu: createApplicationTray(),
},
Menu: Menu.createApplicationMenu(),
//Tray: &menu.Tray{
// Icon: "light",
// Menu: createApplicationTray(),
//},
},
LogLevel: logger.TRACE,
})
@ -46,7 +47,7 @@ func main() {
app.Bind(&System{})
app.Bind(&Dialog{})
app.Bind(&Window{})
app.Bind(&Menu{})
app.Bind(Menu)
app.Bind(&Tray{})
app.Bind(&ContextMenu{})

View File

@ -69,13 +69,13 @@ func (m *Menu) addMenu(mi *menu.MenuItem) {
parent := mi.Parent()
counter := m.incrementcounter()
menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
parent.Append(menu.Text(menuText, menuText, nil))
parent.Append(menu.Text(menuText, menuText, nil, nil))
// parent.Append(menu.Text(menuText, menuText, menu.Key("[")))
// If this is the first dynamic menu added, let's add a remove menu item
if counter == 1 {
removeMenu := menu.Text("Remove "+menuText,
"Remove Last Item", keys.CmdOrCtrl("-"))
"Remove Last Item", keys.CmdOrCtrl("-"), nil)
parent.Prepend(removeMenu)
m.runtime.Menu.On("Remove Last Item", m.removeMenu)
} else {
@ -126,9 +126,9 @@ func (m *Menu) createDynamicMenuTwo() {
// Create our submenu
dm2 := menu.SubMenu("Dynamic Menus 2", menu.NewMenuFromItems(
menu.Text("Insert Before Random Menu Item",
"Insert Before Random", keys.CmdOrCtrl("]")),
"Insert Before Random", keys.CmdOrCtrl("]"), nil),
menu.Text("Insert After Random Menu Item",
"Insert After Random", keys.CmdOrCtrl("[")),
"Insert After Random", keys.CmdOrCtrl("["), nil),
menu.Separator(),
))
@ -142,7 +142,7 @@ func (m *Menu) createDynamicMenuTwo() {
m.anotherDynamicMenuCounter = 5
for index := 0; index < m.anotherDynamicMenuCounter; index++ {
text := "Other Dynamic Menu Item " + strconv.Itoa(index+1)
item := menu.Text(text, text, nil)
item := menu.Text(text, text, nil, nil)
m.dynamicMenuItems[text] = item
dm2.Append(item)
}
@ -176,7 +176,7 @@ func (m *Menu) insertBeforeRandom(_ *menu.MenuItem) {
m.anotherDynamicMenuCounter++
text := "Other Dynamic Menu Item " + strconv.Itoa(
m.anotherDynamicMenuCounter+1)
newItem := menu.Text(text, text, nil)
newItem := menu.Text(text, text, nil, nil)
m.dynamicMenuItems[text] = newItem
item := m.runtime.Menu.GetByID(randomItemID)
@ -211,7 +211,7 @@ func (m *Menu) insertAfterRandom(_ *menu.MenuItem) {
m.anotherDynamicMenuCounter++
text := "Other Dynamic Menu Item " + strconv.Itoa(
m.anotherDynamicMenuCounter+1)
newItem := menu.Text(text, text, nil)
newItem := menu.Text(text, text, nil, nil)
item := m.runtime.Menu.GetByID(randomItemID)
m.dynamicMenuItems[text] = newItem
@ -224,7 +224,11 @@ func (m *Menu) insertAfterRandom(_ *menu.MenuItem) {
m.runtime.Menu.Update()
}
func createApplicationMenu() *menu.Menu {
func (m *Menu) processPlainText(callbackData *menu.CallbackData) {
println("\n\n\n\n\n\n\nCallback successful\n\n\n\n\n")
}
func (m *Menu) createApplicationMenu() *menu.Menu {
// Create menu
myMenu := menu.DefaultMacMenu()
@ -245,62 +249,62 @@ func createApplicationMenu() *menu.Menu {
menu.Front(),
menu.SubMenu("Test Submenu", menu.NewMenuFromItems(
menu.Text("Plain text", "plain text", nil),
menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil),
menu.Text("Plain text", "plain text", nil, m.processPlainText),
menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil, nil),
menu.SubMenu("Accelerators", menu.NewMenuFromItems(
menu.SubMenu("Modifiers", menu.NewMenuFromItems(
menu.Text("Shift accelerator", "Shift", keys.Shift("o")),
menu.Text("Control accelerator", "Control", keys.Control("o")),
menu.Text("Command accelerator", "Command", keys.CmdOrCtrl("o")),
menu.Text("Option accelerator", "Option", keys.OptionOrAlt("o")),
menu.Text("Combo accelerator", "Combo", keys.Combo("o", keys.CmdOrCtrlKey, keys.ShiftKey)),
menu.Text("Shift accelerator", "Shift", keys.Shift("o"), nil),
menu.Text("Control accelerator", "Control", keys.Control("o"), nil),
menu.Text("Command accelerator", "Command", keys.CmdOrCtrl("o"), nil),
menu.Text("Option accelerator", "Option", keys.OptionOrAlt("o"), nil),
menu.Text("Combo accelerator", "Combo", keys.Combo("o", keys.CmdOrCtrlKey, keys.ShiftKey), nil),
)),
menu.SubMenu("System Keys", menu.NewMenuFromItems(
menu.Text("Backspace", "Backspace", keys.Key("Backspace")),
menu.Text("Tab", "Tab", keys.Key("Tab")),
menu.Text("Return", "Return", keys.Key("Return")),
menu.Text("Escape", "Escape", keys.Key("Escape")),
menu.Text("Left", "Left", keys.Key("Left")),
menu.Text("Right", "Right", keys.Key("Right")),
menu.Text("Up", "Up", keys.Key("Up")),
menu.Text("Down", "Down", keys.Key("Down")),
menu.Text("Space", "Space", keys.Key("Space")),
menu.Text("Delete", "Delete", keys.Key("Delete")),
menu.Text("Home", "Home", keys.Key("Home")),
menu.Text("End", "End", keys.Key("End")),
menu.Text("Page Up", "Page Up", keys.Key("Page Up")),
menu.Text("Page Down", "Page Down", keys.Key("Page Down")),
menu.Text("NumLock", "NumLock", keys.Key("NumLock")),
menu.Text("Backspace", "Backspace", keys.Key("Backspace"), nil),
menu.Text("Tab", "Tab", keys.Key("Tab"), nil),
menu.Text("Return", "Return", keys.Key("Return"), nil),
menu.Text("Escape", "Escape", keys.Key("Escape"), nil),
menu.Text("Left", "Left", keys.Key("Left"), nil),
menu.Text("Right", "Right", keys.Key("Right"), nil),
menu.Text("Up", "Up", keys.Key("Up"), nil),
menu.Text("Down", "Down", keys.Key("Down"), nil),
menu.Text("Space", "Space", keys.Key("Space"), nil),
menu.Text("Delete", "Delete", keys.Key("Delete"), nil),
menu.Text("Home", "Home", keys.Key("Home"), nil),
menu.Text("End", "End", keys.Key("End"), nil),
menu.Text("Page Up", "Page Up", keys.Key("Page Up"), nil),
menu.Text("Page Down", "Page Down", keys.Key("Page Down"), nil),
menu.Text("NumLock", "NumLock", keys.Key("NumLock"), nil),
)),
menu.SubMenu("Function Keys", menu.NewMenuFromItems(
menu.Text("F1", "F1", keys.Key("F1")),
menu.Text("F2", "F2", keys.Key("F2")),
menu.Text("F3", "F3", keys.Key("F3")),
menu.Text("F4", "F4", keys.Key("F4")),
menu.Text("F5", "F5", keys.Key("F5")),
menu.Text("F6", "F6", keys.Key("F6")),
menu.Text("F7", "F7", keys.Key("F7")),
menu.Text("F8", "F8", keys.Key("F8")),
menu.Text("F9", "F9", keys.Key("F9")),
menu.Text("F10", "F10", keys.Key("F10")),
menu.Text("F11", "F11", keys.Key("F11")),
menu.Text("F12", "F12", keys.Key("F12")),
menu.Text("F13", "F13", keys.Key("F13")),
menu.Text("F14", "F14", keys.Key("F14")),
menu.Text("F15", "F15", keys.Key("F15")),
menu.Text("F16", "F16", keys.Key("F16")),
menu.Text("F17", "F17", keys.Key("F17")),
menu.Text("F18", "F18", keys.Key("F18")),
menu.Text("F19", "F19", keys.Key("F19")),
menu.Text("F20", "F20", keys.Key("F20")),
menu.Text("F1", "F1", keys.Key("F1"), nil),
menu.Text("F2", "F2", keys.Key("F2"), nil),
menu.Text("F3", "F3", keys.Key("F3"), nil),
menu.Text("F4", "F4", keys.Key("F4"), nil),
menu.Text("F5", "F5", keys.Key("F5"), nil),
menu.Text("F6", "F6", keys.Key("F6"), nil),
menu.Text("F7", "F7", keys.Key("F7"), nil),
menu.Text("F8", "F8", keys.Key("F8"), nil),
menu.Text("F9", "F9", keys.Key("F9"), nil),
menu.Text("F10", "F10", keys.Key("F10"), nil),
menu.Text("F11", "F11", keys.Key("F11"), nil),
menu.Text("F12", "F12", keys.Key("F12"), nil),
menu.Text("F13", "F13", keys.Key("F13"), nil),
menu.Text("F14", "F14", keys.Key("F14"), nil),
menu.Text("F15", "F15", keys.Key("F15"), nil),
menu.Text("F16", "F16", keys.Key("F16"), nil),
menu.Text("F17", "F17", keys.Key("F17"), nil),
menu.Text("F18", "F18", keys.Key("F18"), nil),
menu.Text("F19", "F19", keys.Key("F19"), nil),
menu.Text("F20", "F20", keys.Key("F20"), nil),
)),
menu.SubMenu("Standard Keys", menu.NewMenuFromItems(
menu.Text("Backtick", "Backtick", keys.Key("`")),
menu.Text("Plus", "Plus", keys.Key("+")),
menu.Text("Backtick", "Backtick", keys.Key("`"), nil),
menu.Text("Plus", "Plus", keys.Key("+"), nil),
)),
)),
menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", menu.NewMenuFromItems(
menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+")),
menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+"), nil),
menu.Separator(),
)),
&menu.MenuItem{
@ -321,11 +325,11 @@ func createApplicationMenu() *menu.Menu {
Accelerator: keys.CmdOrCtrl("l"),
Checked: true,
},
menu.Checkbox("Checkbox Menu 2", "checkbox-menu 2", false, nil),
menu.Checkbox("Checkbox Menu 2", "checkbox-menu 2", false, nil, nil),
menu.Separator(),
menu.Radio("😀 Option 1", "😀option-1", true, nil),
menu.Radio("😺 Option 2", "option-2", false, nil),
menu.Radio("❤️ Option 3", "option-3", false, nil),
menu.Radio("😀 Option 1", "😀option-1", true, nil, nil),
menu.Radio("😺 Option 2", "option-2", false, nil, nil),
menu.Radio("❤️ Option 3", "option-3", false, nil, nil),
)),
))

View File

@ -78,13 +78,13 @@ func (t *Tray) addMenu(mi *menu.MenuItem) {
parent := mi.Parent()
counter := t.incrementcounter()
menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
parent.Append(menu.Text(menuText, menuText, nil))
parent.Append(menu.Text(menuText, menuText, nil, nil))
// parent.Append(menu.Text(menuText, menuText, menu.Key("[")))
// If this is the first dynamic menu added, let's add a remove menu item
if counter == 1 {
removeMenu := menu.Text("Remove "+menuText,
"Remove Last Item", keys.CmdOrCtrl("-"))
"Remove Last Item", keys.CmdOrCtrl("-"), nil)
parent.Prepend(removeMenu)
t.runtime.Tray.On("Remove Last Item", t.removeMenu)
} else {
@ -136,9 +136,9 @@ func (t *Tray) SetIcon(trayIconID string) {
func createApplicationTray() *menu.Menu {
trayMenu := &menu.Menu{}
trayMenu.Append(menu.Text("Show Window", "Show Window", nil))
trayMenu.Append(menu.Text("Hide Window", "Hide Window", nil))
trayMenu.Append(menu.Text("Minimise Window", "Minimise Window", nil))
trayMenu.Append(menu.Text("Unminimise Window", "Unminimise Window", nil))
trayMenu.Append(menu.Text("Show Window", "Show Window", nil, nil))
trayMenu.Append(menu.Text("Hide Window", "Hide Window", nil, nil))
trayMenu.Append(menu.Text("Minimise Window", "Minimise Window", nil, nil))
trayMenu.Append(menu.Text("Unminimise Window", "Unminimise Window", nil, nil))
return trayMenu
}