5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 19:51:15 +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/binding"
"github.com/wailsapp/wails/v2/internal/ffenestri" "github.com/wailsapp/wails/v2/internal/ffenestri"
"github.com/wailsapp/wails/v2/internal/logger" "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/messagedispatcher"
"github.com/wailsapp/wails/v2/internal/runtime" "github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus" "github.com/wailsapp/wails/v2/internal/servicebus"
@ -33,6 +34,8 @@ type App struct {
contextmenus *subsystem.ContextMenus contextmenus *subsystem.ContextMenus
dispatcher *messagedispatcher.Dispatcher dispatcher *messagedispatcher.Dispatcher
menuManager *menumanager.Manager
// Indicates if the app is in debug mode // Indicates if the app is in debug mode
debug bool debug bool
@ -54,13 +57,18 @@ func CreateApp(appoptions *options.App) (*App, error) {
myLogger := logger.New(appoptions.Logger) myLogger := logger.New(appoptions.Logger)
myLogger.SetLogLevel(appoptions.LogLevel) 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{ result := &App{
window: window, window: window,
servicebus: servicebus.New(myLogger), servicebus: servicebus.New(myLogger),
logger: myLogger, logger: myLogger,
bindings: binding.NewBindings(myLogger), bindings: binding.NewBindings(myLogger),
menuManager: menuManager,
} }
result.options = appoptions result.options = appoptions
@ -157,7 +165,7 @@ func (a *App) Run() error {
// Optionally start the menu subsystem // Optionally start the menu subsystem
if applicationMenu != nil { 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 { if err != nil {
return err return err
} }

View File

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

View File

@ -76,23 +76,10 @@ func (a *Application) processPlatformSettings() error {
} }
// Process menu // Process menu
applicationMenu := options.GetApplicationMenu(a.config) //applicationMenu := options.GetApplicationMenu(a.config)
if applicationMenu != nil { applicationMenu := a.menuManager.GetApplicationMenuJSON()
if applicationMenu != "" {
/* C.SetMenu(a.app, a.string2CString(applicationMenu))
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)))
} }
// Process tray // 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 package subsystem
import ( import (
"github.com/wailsapp/wails/v2/internal/menumanager"
"strings" "strings"
"sync" "sync"
@ -35,15 +36,15 @@ type Menu struct {
// logger // logger
logger logger.CustomLogger logger logger.CustomLogger
// The application menu
applicationMenu *menu.Menu
// Service Bus // Service Bus
bus *servicebus.ServiceBus bus *servicebus.ServiceBus
// Menu Manager
menuManager *menumanager.Manager
} }
// NewMenu creates a new menu subsystem // 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 // Register quit channel
quitChannel, err := bus.Subscribe("quit") quitChannel, err := bus.Subscribe("quit")
@ -58,18 +59,15 @@ func NewMenu(applicationMenu *menu.Menu, bus *servicebus.ServiceBus, logger *log
} }
result := &Menu{ result := &Menu{
quitChannel: quitChannel, quitChannel: quitChannel,
menuChannel: menuChannel, menuChannel: menuChannel,
logger: logger.CustomLogger("Menu Subsystem"), logger: logger.CustomLogger("Menu Subsystem"),
listeners: make(map[string][]func(*menu.MenuItem)), listeners: make(map[string][]func(*menu.MenuItem)),
menuItems: make(map[string]*menu.MenuItem), menuItems: make(map[string]*menu.MenuItem),
applicationMenu: applicationMenu, bus: bus,
bus: bus, menuManager: menuManager,
} }
// Build up list of item/id pairs
result.processMenu(applicationMenu)
return result, nil 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()) m.logger.Trace("Got Menu clicked Message: %s %+v", menuMessage.Topic(), menuMessage.Data())
menuid := menuMessage.Data().(string) menuid := menuMessage.Data().(string)
// Get the menu item err := m.menuManager.ProcessClick(menuid)
menuItem := m.menuItems[menuid] if err != nil {
if menuItem == nil { m.logger.Trace("%s", err.Error())
m.logger.Trace("Cannot process menuid %s - unknown", menuid)
return
} }
// 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": case "on":
listenerDetails := menuMessage.Data().(*message.MenuOnMessage) listenerDetails := menuMessage.Data().(*message.MenuOnMessage)
id := listenerDetails.MenuID id := listenerDetails.MenuID
@ -121,11 +109,11 @@ func (m *Menu) Start() error {
// Make sure we catch any menu updates // Make sure we catch any menu updates
case "update": case "update":
updatedMenu := menuMessage.Data().(*menu.Menu) //updatedMenu := menuMessage.Data().(*menu.Menu)
m.processMenu(updatedMenu) //m.processMenu(updatedMenu)
//
// Notify frontend of menu change //// Notify frontend of menu change
m.bus.Publish("menufrontend:update", updatedMenu) //m.bus.Publish("menufrontend:update", updatedMenu)
default: default:
m.logger.Error("unknown menu message: %+v", menuMessage) m.logger.Error("unknown menu message: %+v", menuMessage)
@ -140,56 +128,6 @@ func (m *Menu) Start() error {
return nil 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() { func (m *Menu) shutdown() {
m.logger.Trace("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 []*MenuItem `json:"SubMenu,omitempty"`
SubMenu *Menu `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 colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red
Foreground int 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 // 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{ return &MenuItem{
ID: id, ID: id,
Label: label, Label: label,
Type: TextType, Type: TextType,
Accelerator: accelerator, Accelerator: accelerator,
Click: click,
} }
} }
@ -243,24 +247,26 @@ func Separator() *MenuItem {
} }
// Radio is a helper to create basic Radio menu items with an accelerator // 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{ return &MenuItem{
ID: id, ID: id,
Label: label, Label: label,
Type: RadioType, Type: RadioType,
Checked: selected, Checked: selected,
Accelerator: accelerator, Accelerator: accelerator,
Click: click,
} }
} }
// Checkbox is a helper to create basic Checkbox menu items // 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{ return &MenuItem{
ID: id, ID: id,
Label: label, Label: label,
Type: CheckboxType, Type: CheckboxType,
Checked: checked, Checked: checked,
Accelerator: accelerator, Accelerator: accelerator,
Click: click,
} }
} }

View File

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

View File

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