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:
parent
3a2d01813a
commit
8ee8c9b07c
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
54
v2/internal/menumanager/menuitemmap.go
Normal file
54
v2/internal/menumanager/menuitemmap.go
Normal 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
|
||||
}
|
82
v2/internal/menumanager/menumanager.go
Normal file
82
v2/internal/menumanager/menumanager.go
Normal 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
|
||||
}
|
156
v2/internal/menumanager/processedMenu.go
Normal file
156
v2/internal/menumanager/processedMenu.go
Normal 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{}
|
||||
}
|
||||
}
|
@ -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
8
v2/pkg/menu/callback.go
Normal file
@ -0,0 +1,8 @@
|
||||
package menu
|
||||
|
||||
type CallbackData struct {
|
||||
MenuItem *MenuItem
|
||||
ContextData string
|
||||
}
|
||||
|
||||
type Callback func(*CallbackData)
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{})
|
||||
|
||||
|
@ -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),
|
||||
)),
|
||||
))
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user