Removal of menu IDs. WARNING: context menus are currently broken

This commit is contained in:
Lea Anthony
2021-01-13 21:14:44 +11:00
parent 3045ec107f
commit a86fbbb440
17 changed files with 103 additions and 940 deletions

View File

@@ -24,15 +24,13 @@ type App struct {
options *options.App
// Subsystems
log *subsystem.Log
runtime *subsystem.Runtime
event *subsystem.Event
binding *subsystem.Binding
call *subsystem.Call
menu *subsystem.Menu
tray *subsystem.Tray
contextmenus *subsystem.ContextMenus
dispatcher *messagedispatcher.Dispatcher
log *subsystem.Log
runtime *subsystem.Runtime
event *subsystem.Event
binding *subsystem.Binding
call *subsystem.Call
menu *subsystem.Menu
dispatcher *messagedispatcher.Dispatcher
menuManager *menumanager.Manager
@@ -191,32 +189,6 @@ func (a *App) Run() error {
}
}
//// Optionally start the tray subsystem
//if trayMenu != nil {
// traysubsystem, err := subsystem.NewTray(trayMenu, a.servicebus, a.logger)
// if err != nil {
// return err
// }
// a.tray = traysubsystem
// err = a.tray.Start()
// if err != nil {
// return err
// }
//}
// Optionally start the context menu subsystem
if contextMenus != nil {
contextmenussubsystem, err := subsystem.NewContextMenus(contextMenus, a.servicebus, a.logger)
if err != nil {
return err
}
a.contextmenus = contextmenussubsystem
err = a.contextmenus.Start()
if err != nil {
return err
}
}
// Start the call subsystem
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
if err != nil {

View File

@@ -1,9 +1,7 @@
package ffenestri
import (
"encoding/json"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/pkg/menu"
"runtime"
"strings"
"unsafe"
@@ -185,28 +183,3 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
func messageFromWindowCallback(data *C.char) {
dispatcher.DispatchMessage(C.GoString(data))
}
type ProcessedContextMenu struct {
ID string
ProcessedMenu *ProcessedMenu
}
func processContextMenus(contextMenus *menu.ContextMenus) (string, error) {
var processedContextMenus []*ProcessedContextMenu
// We need to iterate each context menu and pre-process it
for contextMenuID, contextMenu := range contextMenus.Items {
thisContextMenu := &ProcessedContextMenu{
ID: contextMenuID,
ProcessedMenu: NewProcessedMenu(contextMenu),
}
processedContextMenus = append(processedContextMenus, thisContextMenu)
}
contextMenusJSON, err := json.Marshal(processedContextMenus)
if err != nil {
return "", err
}
return string(contextMenusJSON), nil
}

View File

@@ -12,7 +12,6 @@ package ffenestri
import "C"
import (
"encoding/json"
"github.com/wailsapp/wails/v2/pkg/menu"
"strconv"
@@ -190,22 +189,6 @@ func (c *Client) UpdateMenu(menuJSON string) {
C.SetApplicationMenu(c.app.app, c.app.string2CString(menuJSON))
}
func (c *Client) UpdateTray(menu *menu.Menu) {
// Guard against nil menus
if menu == nil {
return
}
// Process the menu
processedMenu := NewProcessedMenu(menu)
trayMenuJSON, err := json.Marshal(processedMenu)
if err != nil {
c.app.logger.Error("Error processing updated TrayMenu: %s", err.Error())
return
}
C.UpdateTray(c.app.app, c.app.string2CString(string(trayMenuJSON)))
}
func (c *Client) UpdateTrayMenu(trayMenuJSON string) {
C.UpdateTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON))
}
@@ -219,16 +202,16 @@ func (c *Client) UpdateTrayIcon(name string) {
}
func (c *Client) UpdateContextMenus(contextMenus *menu.ContextMenus) {
// Guard against nil contextMenus
if contextMenus == nil {
return
}
// Process the menu
contextMenusJSON, err := processContextMenus(contextMenus)
if err != nil {
c.app.logger.Error("Error processing updated Context Menu: %s", err.Error())
return
}
C.UpdateContextMenus(c.app.app, c.app.string2CString(contextMenusJSON))
//
// // Guard against nil contextMenus
// if contextMenus == nil {
// return
// }
// // Process the menu
// contextMenusJSON, err := processContextMenus(contextMenus)
// if err != nil {
// c.app.logger.Error("Error processing updated Context Menu: %s", err.Error())
// return
// }
// C.UpdateContextMenus(c.app.app, c.app.string2CString(contextMenusJSON))
}

View File

@@ -7,13 +7,8 @@ package ffenestri
#include "ffenestri.h"
#include "ffenestri_darwin.h"
*/
import "C"
import (
"github.com/wailsapp/wails/v2/pkg/options"
)
func (a *Application) processPlatformSettings() error {
@@ -83,14 +78,14 @@ func (a *Application) processPlatformSettings() error {
}
// Process context menus
contextMenus := options.GetContextMenus(a.config)
if contextMenus != nil {
contextMenusJSON, err := processContextMenus(contextMenus)
if err != nil {
return err
}
C.SetContextMenus(a.app, a.string2CString(contextMenusJSON))
}
//contextMenus := options.GetContextMenus(a.config)
//if contextMenus != nil {
// contextMenusJSON, err := processContextMenus(contextMenus)
// if err != nil {
// return err
// }
// C.SetContextMenus(a.app, a.string2CString(contextMenusJSON))
//}
return nil
}

View File

@@ -1,80 +0,0 @@
package ffenestri
import "github.com/wailsapp/wails/v2/pkg/menu"
// ProcessedMenu is the original menu with the addition
// of radio groups extracted from the menu data
type ProcessedMenu struct {
Menu *menu.Menu
RadioGroups []*RadioGroup
currentRadioGroup []string
}
// RadioGroup holds all the members of the same radio group
type RadioGroup struct {
Members []string
Length int
}
// NewProcessedMenu processed the given menu and returns
// the original menu with the extracted radio groups
func NewProcessedMenu(menu *menu.Menu) *ProcessedMenu {
result := &ProcessedMenu{
Menu: menu,
RadioGroups: []*RadioGroup{},
currentRadioGroup: []string{},
}
result.processMenu()
return result
}
func (p *ProcessedMenu) processMenu() {
// Loop over top level menus
for _, item := range p.Menu.Items {
// Process MenuItem
p.processMenuItem(item)
}
p.finaliseRadioGroup()
}
func (p *ProcessedMenu) processMenuItem(item *menu.MenuItem) {
switch item.Type {
// We need to recurse submenus
case menu.SubmenuType:
// Finalise any current radio groups as they don't trickle down to submenus
p.finaliseRadioGroup()
// Process each submenu item
for _, subitem := range item.SubMenu.Items {
p.processMenuItem(subitem)
}
case menu.RadioType:
// Add the item to the radio group
p.currentRadioGroup = append(p.currentRadioGroup, item.ID)
default:
p.finaliseRadioGroup()
}
}
func (p *ProcessedMenu) finaliseRadioGroup() {
// If we were processing a radio group, fix up the references
if len(p.currentRadioGroup) > 0 {
// Create new radiogroup
group := &RadioGroup{
Members: p.currentRadioGroup,
Length: len(p.currentRadioGroup),
}
p.RadioGroups = append(p.RadioGroups, group)
// Empty the radio group
p.currentRadioGroup = []string{}
}
}

View File

@@ -33,7 +33,6 @@ type Client interface {
WindowSetColour(colour int)
DarkModeEnabled(callbackID string)
UpdateMenu(menuJSON string)
UpdateTray(menu *menu.Menu)
UpdateTrayLabel(label string)
UpdateTrayIcon(name string)
UpdateTrayMenu(menuJSON string)

View File

@@ -25,7 +25,6 @@ type Dispatcher struct {
systemChannel <-chan *servicebus.Message
menuChannel <-chan *servicebus.Message
contextMenuChannel <-chan *servicebus.Message
trayChannel <-chan *servicebus.Message
running bool
servicebus *servicebus.ServiceBus
@@ -83,11 +82,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
return nil, err
}
trayChannel, err := servicebus.Subscribe("trayfrontend:")
if err != nil {
return nil, err
}
result := &Dispatcher{
servicebus: servicebus,
eventChannel: eventChannel,
@@ -99,7 +93,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
dialogChannel: dialogChannel,
systemChannel: systemChannel,
menuChannel: menuChannel,
trayChannel: trayChannel,
contextMenuChannel: contextMenuChannel,
}
@@ -134,8 +127,6 @@ func (d *Dispatcher) Start() error {
d.processMenuMessage(menuMessage)
case contextMenuMessage := <-d.contextMenuChannel:
d.processContextMenuMessage(contextMenuMessage)
case trayMessage := <-d.trayChannel:
d.processTrayMessage(trayMessage)
}
}
@@ -510,62 +501,3 @@ func (d *Dispatcher) processContextMenuMessage(result *servicebus.Message) {
d.logger.Error("Unknown contextmenufrontend command: %s", command)
}
}
func (d *Dispatcher) processTrayMessage(result *servicebus.Message) {
splitTopic := strings.Split(result.Topic(), ":")
if len(splitTopic) < 2 {
d.logger.Error("Invalid tray message : %#v", result.Data())
return
}
command := splitTopic[1]
switch command {
case "update":
updatedMenu, ok := result.Data().(*menu.Menu)
if !ok {
d.logger.Error("Invalid data for 'trayfrontend:update' : %#v",
result.Data())
return
}
// TODO: Work out what we mean in a multi window environment...
// For now we will just pick the first one
for _, client := range d.clients {
client.frontend.UpdateTray(updatedMenu)
}
case "setlabel":
updatedLabel, ok := result.Data().(string)
if !ok {
d.logger.Error("Invalid data for 'trayfrontend:setlabel' : %#v",
result.Data())
return
}
// TODO: Work out what we mean in a multi window environment...
// For now we will just pick the first one
for _, client := range d.clients {
client.frontend.UpdateTrayLabel(updatedLabel)
}
case "seticon":
iconname, ok := result.Data().(string)
if !ok {
d.logger.Error("Invalid data for 'trayfrontend:seticon' : %#v",
result.Data())
return
}
// TODO: Work out what we mean in a multi window environment...
// For now we will just pick the first one
for _, client := range d.clients {
client.frontend.UpdateTrayIcon(iconname)
}
default:
d.logger.Error("Unknown menufrontend command: %s", command)
}
}

View File

@@ -1,48 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// ContextMenus defines all ContextMenu related operations
type ContextMenus interface {
On(menuID string, callback func(*menu.MenuItem, string))
Update()
GetByID(menuID string) *menu.MenuItem
RemoveByID(id string) bool
}
type contextMenus struct {
bus *servicebus.ServiceBus
contextmenus *menu.ContextMenus
}
// newContextMenus creates a new ContextMenu struct
func newContextMenus(bus *servicebus.ServiceBus, contextmenus *menu.ContextMenus) ContextMenus {
return &contextMenus{
bus: bus,
contextmenus: contextmenus,
}
}
// On registers a listener for a particular event
func (t *contextMenus) On(menuID string, callback func(*menu.MenuItem, string)) {
t.bus.Publish("contextmenus:on", &message.ContextMenusOnMessage{
MenuID: menuID,
Callback: callback,
})
}
func (t *contextMenus) Update() {
t.bus.Publish("contextmenus:update", t.contextmenus)
}
func (t *contextMenus) GetByID(menuItemID string) *menu.MenuItem {
return t.contextmenus.GetByID(menuItemID)
}
func (t *contextMenus) RemoveByID(menuItemID string) bool {
return t.contextmenus.RemoveByID(menuItemID)
}

View File

@@ -1,59 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// Tray defines all Tray related operations
type Tray interface {
On(menuID string, callback func(*menu.MenuItem))
Update(tray ...*menu.TrayMenu)
GetByID(menuID string) *menu.MenuItem
RemoveByID(id string) bool
SetLabel(label string)
SetIcon(name string)
}
type trayRuntime struct {
bus *servicebus.ServiceBus
trayMenu *menu.TrayMenu
}
// newTray creates a new Menu struct
func newTray(bus *servicebus.ServiceBus, menu *menu.TrayMenu) Tray {
return &trayRuntime{
bus: bus,
trayMenu: menu,
}
}
// On registers a listener for a particular event
func (t *trayRuntime) On(menuID string, callback func(*menu.MenuItem)) {
t.bus.Publish("tray:on", &message.TrayOnMessage{
MenuID: menuID,
Callback: callback,
})
}
func (t *trayRuntime) Update(tray ...*menu.TrayMenu) {
//trayToUpdate := t.trayMenu
t.bus.Publish("tray:update", t.trayMenu)
}
func (t *trayRuntime) SetLabel(label string) {
t.bus.Publish("tray:setlabel", label)
}
func (t *trayRuntime) SetIcon(name string) {
t.bus.Publish("tray:seticon", name)
}
func (t *trayRuntime) GetByID(menuID string) *menu.MenuItem {
return t.trayMenu.Menu.GetByID(menuID)
}
func (t *trayRuntime) RemoveByID(id string) bool {
return t.trayMenu.Menu.RemoveByID(id)
}

View File

@@ -1,200 +0,0 @@
package subsystem
import (
"encoding/json"
"strings"
"sync"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// ContextMenus is the subsystem that handles the operation of context menus. It manages all service bus messages
// starting with "contextmenus".
type ContextMenus struct {
quitChannel <-chan *servicebus.Message
menuChannel <-chan *servicebus.Message
running bool
// Event listeners
listeners map[string][]func(*menu.MenuItem, string)
menuItems map[string]*menu.MenuItem
notifyLock sync.RWMutex
// logger
logger logger.CustomLogger
// The context menus
contextMenus *menu.ContextMenus
// Service Bus
bus *servicebus.ServiceBus
}
// NewContextMenus creates a new context menu subsystem
func NewContextMenus(contextMenus *menu.ContextMenus, bus *servicebus.ServiceBus, logger *logger.Logger) (*ContextMenus, error) {
// Register quit channel
quitChannel, err := bus.Subscribe("quit")
if err != nil {
return nil, err
}
// Subscribe to menu messages
menuChannel, err := bus.Subscribe("contextmenus:")
if err != nil {
return nil, err
}
result := &ContextMenus{
quitChannel: quitChannel,
menuChannel: menuChannel,
logger: logger.CustomLogger("Context Menu Subsystem"),
listeners: make(map[string][]func(*menu.MenuItem, string)),
menuItems: make(map[string]*menu.MenuItem),
contextMenus: contextMenus,
bus: bus,
}
// Build up list of item/id pairs
result.processContextMenus(contextMenus)
return result, nil
}
type contextMenuData struct {
MenuItemID string `json:"menuItemID"`
Data string `json:"data"`
}
// Start the subsystem
func (c *ContextMenus) Start() error {
c.logger.Trace("Starting")
c.running = true
// Spin off a go routine
go func() {
for c.running {
select {
case <-c.quitChannel:
c.running = false
break
case menuMessage := <-c.menuChannel:
splitTopic := strings.Split(menuMessage.Topic(), ":")
menuMessageType := splitTopic[1]
switch menuMessageType {
case "clicked":
if len(splitTopic) != 2 {
c.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
continue
}
c.logger.Trace("Got Context Menu clicked Message: %s %+v", menuMessage.Topic(), menuMessage.Data())
contextMenuDataJSON := menuMessage.Data().(string)
var data contextMenuData
err := json.Unmarshal([]byte(contextMenuDataJSON), &data)
if err != nil {
c.logger.Trace("Cannot process contextMenuDataJSON %s", string(contextMenuDataJSON))
return
}
// Get the menu item
menuItem := c.menuItems[data.MenuItemID]
if menuItem == nil {
c.logger.Trace("Cannot process menuitem id %s - unknown", data.MenuItemID)
return
}
// Is the menu item a checkbox?
if menuItem.Type == menu.CheckboxType {
// Toggle state
menuItem.Checked = !menuItem.Checked
}
// Notify listeners
c.notifyListeners(data, menuItem)
case "on":
listenerDetails := menuMessage.Data().(*message.ContextMenusOnMessage)
id := listenerDetails.MenuID
c.listeners[id] = append(c.listeners[id], listenerDetails.Callback)
// Make sure we catch any menu updates
case "update":
updatedMenu := menuMessage.Data().(*menu.ContextMenus)
c.processContextMenus(updatedMenu)
// Notify frontend of menu change
c.bus.Publish("contextmenufrontend:update", updatedMenu)
default:
c.logger.Error("unknown context menu message: %+v", menuMessage)
}
}
}
// Call shutdown
c.shutdown()
}()
return nil
}
func (c *ContextMenus) processContextMenus(contextMenus *menu.ContextMenus) {
// Initialise the variables
c.menuItems = make(map[string]*menu.MenuItem)
c.contextMenus = contextMenus
for _, contextMenu := range contextMenus.Items {
for _, item := range contextMenu.Items {
c.processMenuItem(item)
}
}
}
func (c *ContextMenus) processMenuItem(item *menu.MenuItem) {
if item.SubMenu != nil {
for _, submenuitem := range item.SubMenu.Items {
c.processMenuItem(submenuitem)
}
return
}
if item.ID != "" {
if c.menuItems[item.ID] != nil {
c.logger.Error("Context Menu id '%s' is used by multiple menu items: %s %s", c.menuItems[item.ID].Label, item.Label)
return
}
c.menuItems[item.ID] = item
}
}
// Notifies listeners that the given menu was clicked
func (c *ContextMenus) notifyListeners(contextData contextMenuData, menuItem *menu.MenuItem) {
// Get list of menu listeners
listeners := c.listeners[contextData.MenuItemID]
if listeners == nil {
c.logger.Trace("No listeners for MenuItem with ID '%s'", contextData.MenuItemID)
return
}
// Lock the listeners
c.notifyLock.Lock()
// Callback in goroutine
for _, listener := range listeners {
go listener(menuItem, contextData.Data)
}
// Unlock
c.notifyLock.Unlock()
}
func (c *ContextMenus) shutdown() {
c.logger.Trace("Shutdown")
}

View File

@@ -1,202 +0,0 @@
package subsystem
import (
"strings"
"sync"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// Tray is the subsystem that handles the operation of the tray menu.
// It manages all service bus messages starting with "tray".
type Tray struct {
quitChannel <-chan *servicebus.Message
trayChannel <-chan *servicebus.Message
running bool
// Event listeners
listeners map[string][]func(*menu.MenuItem)
menuItems map[string]*menu.MenuItem
notifyLock sync.RWMutex
// logger
logger logger.CustomLogger
// The tray menu
trayMenu *menu.TrayMenu
// Service Bus
bus *servicebus.ServiceBus
}
// NewTray creates a new menu subsystem
func NewTray(trayMenu *menu.TrayMenu, bus *servicebus.ServiceBus,
logger *logger.Logger) (*Tray, error) {
// Register quit channel
quitChannel, err := bus.Subscribe("quit")
if err != nil {
return nil, err
}
// Subscribe to menu messages
trayChannel, err := bus.Subscribe("tray:")
if err != nil {
return nil, err
}
result := &Tray{
quitChannel: quitChannel,
trayChannel: trayChannel,
logger: logger.CustomLogger("TrayMenu Subsystem"),
listeners: make(map[string][]func(*menu.MenuItem)),
menuItems: make(map[string]*menu.MenuItem),
trayMenu: trayMenu,
bus: bus,
}
// Build up list of item/id pairs
result.processMenu(trayMenu.Menu)
return result, nil
}
// Start the subsystem
func (t *Tray) Start() error {
t.logger.Trace("Starting")
t.running = true
// Spin off a go routine
go func() {
for t.running {
select {
case <-t.quitChannel:
t.running = false
break
case menuMessage := <-t.trayChannel:
splitTopic := strings.Split(menuMessage.Topic(), ":")
menuMessageType := splitTopic[1]
switch menuMessageType {
case "clicked":
if len(splitTopic) != 2 {
t.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
continue
}
t.logger.Trace("Got TrayMenu Menu clicked Message: %s %+v", menuMessage.Topic(), menuMessage.Data())
menuid := menuMessage.Data().(string)
// Get the menu item
menuItem := t.menuItems[menuid]
if menuItem == nil {
t.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
t.notifyListeners(menuid, menuItem)
case "on":
listenerDetails := menuMessage.Data().(*message.TrayOnMessage)
id := listenerDetails.MenuID
t.listeners[id] = append(t.listeners[id], listenerDetails.Callback)
// Make sure we catch any menu updates
case "update":
updatedMenu := menuMessage.Data().(*menu.Menu)
t.processMenu(updatedMenu)
// Notify frontend of menu change
t.bus.Publish("trayfrontend:update", updatedMenu)
// Make sure we catch any menu updates
case "setlabel":
updatedLabel := menuMessage.Data().(string)
t.trayMenu.Label = updatedLabel
// Notify frontend of menu change
t.bus.Publish("trayfrontend:setlabel", updatedLabel)
// Make sure we catch any icon updates
case "seticon":
iconname := menuMessage.Data().(string)
t.trayMenu.Label = iconname
// Notify frontend of menu change
t.bus.Publish("trayfrontend:seticon", iconname)
default:
t.logger.Error("unknown tray message: %+v", menuMessage)
}
}
}
// Call shutdown
t.shutdown()
}()
return nil
}
func (t *Tray) processMenu(trayMenu *menu.Menu) {
// Initialise the variables
t.menuItems = make(map[string]*menu.MenuItem)
t.trayMenu.Menu = trayMenu
for _, item := range trayMenu.Items {
t.processMenuItem(item)
}
}
func (t *Tray) processMenuItem(item *menu.MenuItem) {
if item.SubMenu != nil {
for _, submenuitem := range item.SubMenu.Items {
t.processMenuItem(submenuitem)
}
return
}
if item.ID != "" {
if t.menuItems[item.ID] != nil {
t.logger.Error("Menu id '%s' is used by multiple menu items: %s %s", t.menuItems[item.ID].Label, item.Label)
return
}
t.menuItems[item.ID] = item
}
}
// Notifies listeners that the given menu was clicked
func (t *Tray) notifyListeners(menuid string, menuItem *menu.MenuItem) {
// Get list of menu listeners
listeners := t.listeners[menuid]
if listeners == nil {
t.logger.Trace("No listeners for MenuItem with ID '%s'", menuid)
return
}
// Lock the listeners
t.notifyLock.Lock()
// Callback in goroutine
for _, listener := range listeners {
go listener(menuItem)
}
// Unlock
t.notifyLock.Unlock()
}
func (t *Tray) shutdown() {
t.logger.Trace("Shutdown")
}

View File

@@ -14,29 +14,6 @@ func (c *ContextMenus) AddMenu(ID string, menu *Menu) {
c.Items[ID] = menu
}
func (c *ContextMenus) GetByID(menuID string) *MenuItem {
// Loop over menu items
for _, item := range c.Items {
result := item.GetByID(menuID)
if result != nil {
return result
}
}
return nil
}
func (c *ContextMenus) RemoveByID(id string) bool {
// Loop over menu items
for _, item := range c.Items {
result := item.RemoveByID(id)
if result == true {
return result
}
}
return false
}
type ContextMenu struct {
ID string
Menu *Menu

View File

@@ -27,33 +27,6 @@ func NewMenuFromItems(first *MenuItem, rest ...*MenuItem) *Menu {
return result
}
func (m *Menu) GetByID(menuID string) *MenuItem {
// Loop over menu items
for _, item := range m.Items {
result := item.getByID(menuID)
if result != nil {
return result
}
}
return nil
}
func (m *Menu) RemoveByID(id string) bool {
// Loop over menu items
for index, item := range m.Items {
if item.ID == id {
m.Items = append(m.Items[:index], m.Items[index+1:]...)
return true
}
result := item.removeByID(id)
if result == true {
return result
}
}
return false
}
func (m *Menu) setParent(menuItem *MenuItem) {
for _, item := range m.Items {
item.parent = menuItem

View File

@@ -7,8 +7,6 @@ import (
// MenuItem represents a menuitem contained in a menu
type MenuItem struct {
// The unique identifier of this menu item
ID string `json:"ID,omitempty"`
// Label is what appears as the menu text
Label string
// Role is a predefined menu type
@@ -75,29 +73,6 @@ func (m *MenuItem) Prepend(item *MenuItem) bool {
return true
}
func (m *MenuItem) getByID(id string) *MenuItem {
// If I have the ID return me!
if m.ID == id {
return m
}
// If we have no submenu then exit early
if m.SubMenu == nil {
return nil
}
// Check submenus
for _, submenu := range m.SubMenu.Items {
result := submenu.getByID(id)
if result != nil {
return result
}
}
return nil
}
func (m *MenuItem) Remove() {
// Iterate my parent's children
m.Parent().removeChild(m)
@@ -113,28 +88,6 @@ func (m *MenuItem) removeChild(item *MenuItem) {
m.removeLock.Unlock()
}
func (m *MenuItem) removeByID(id string) bool {
// If we have no submenu, return
if m.SubMenu == nil {
return false
}
for index, item := range m.SubMenu.Items {
if item.ID == id {
m.SubMenu.Items = append(m.SubMenu.Items[:index], m.SubMenu.Items[index+1:]...)
return true
}
if item.isSubMenu() {
result := item.removeByID(id)
if result == true {
return result
}
}
}
return false
}
// InsertAfter attempts to add the given item after this item in the parent
// menu. If there is no parent menu (we are a top level menu) then false is
// returned
@@ -250,9 +203,8 @@ 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, click Callback) *MenuItem {
func Text(label string, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: TextType,
Accelerator: accelerator,
@@ -268,9 +220,8 @@ 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, click Callback) *MenuItem {
func Radio(label string, selected bool, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: RadioType,
Checked: selected,
@@ -280,9 +231,8 @@ func Radio(label string, id string, selected bool, accelerator *keys.Accelerator
}
// Checkbox is a helper to create basic Checkbox menu items
func Checkbox(label string, id string, checked bool, accelerator *keys.Accelerator, click Callback) *MenuItem {
func Checkbox(label string, checked bool, accelerator *keys.Accelerator, click Callback) *MenuItem {
return &MenuItem{
ID: id,
Label: label,
Type: CheckboxType,
Checked: checked,
@@ -305,11 +255,10 @@ func SubMenu(label string, menu *Menu) *MenuItem {
}
// SubMenuWithID is a helper to create Submenus with an ID
func SubMenuWithID(label string, id string, menu *Menu) *MenuItem {
func SubMenuWithID(label string, menu *Menu) *MenuItem {
result := &MenuItem{
Label: label,
SubMenu: menu,
ID: id,
Type: SubmenuType,
}

View File

@@ -25,16 +25,16 @@ func (c *ContextMenu) WailsInit(runtime *wails.Runtime) error {
func createContextMenus() *menu.ContextMenus {
result := menu.NewContextMenus()
result.AddMenu("test", menu.NewMenuFromItems(
menu.Text("Clicked 0 times", "Test Context Menu", nil, nil),
menu.Text("Clicked 0 times", nil, nil),
menu.Separator(),
menu.Checkbox("I am a checkbox", "checkbox", false, nil, nil),
menu.Checkbox("I am a checkbox", false, nil, nil),
menu.Separator(),
menu.Radio("Radio Option 1", "Radio Option 1", true, nil, nil),
menu.Radio("Radio Option 2", "Radio Option 2", false, nil, nil),
menu.Radio("Radio Option 3", "Radio Option 3", false, nil, nil),
menu.Radio("Radio Option 1", true, nil, nil),
menu.Radio("Radio Option 2", false, nil, nil),
menu.Radio("Radio Option 3", false, nil, nil),
menu.Separator(),
menu.SubMenu("A Submenu", menu.NewMenuFromItems(
menu.Text("Hello", "Hello", nil, nil),
menu.Text("Hello", nil, nil),
)),
))
return result

View File

@@ -54,13 +54,13 @@ func (m *Menu) addDynamicMenusOneMenu(data *menu.CallbackData) {
parent := mi.Parent()
counter := m.incrementcounter()
menuText := "Dynamic Menu Item " + strconv.Itoa(counter)
newDynamicMenu := menu.Text(menuText, menuText, nil, nil)
newDynamicMenu := menu.Text(menuText, nil, nil)
m.dynamicMenuOneItems = append(m.dynamicMenuOneItems, newDynamicMenu)
parent.Append(newDynamicMenu)
// If this is the first dynamic menu added, let's add a remove menu item
if counter == 1 {
m.removeMenuItem = menu.Text("Remove "+menuText, "Remove Last Item", keys.CmdOrCtrl("-"), m.removeDynamicMenuOneMenu)
m.removeMenuItem = menu.Text("Remove "+menuText, keys.CmdOrCtrl("-"), m.removeDynamicMenuOneMenu)
parent.Prepend(m.removeMenuItem)
} else {
// Test if the remove menu hasn't already been removed in another thread
@@ -111,8 +111,8 @@ func (m *Menu) createDynamicMenuTwo(data *menu.CallbackData) {
// Create our submenu
dm2 := menu.SubMenu("Dynamic Menus 2", menu.NewMenuFromItems(
menu.Text("Insert Before Random Menu Item", "Insert Before Random", keys.CmdOrCtrl("]"), m.insertBeforeRandom),
menu.Text("Insert After Random Menu Item", "Insert After Random", keys.CmdOrCtrl("["), m.insertAfterRandom),
menu.Text("Insert Before Random Menu Item", keys.CmdOrCtrl("]"), m.insertBeforeRandom),
menu.Text("Insert After Random Menu Item", keys.CmdOrCtrl("["), m.insertAfterRandom),
menu.Separator(),
))
@@ -126,7 +126,7 @@ func (m *Menu) createDynamicMenuTwo(data *menu.CallbackData) {
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, nil)
item := menu.Text(text, nil, nil)
m.dynamicMenuItems[text] = item
dm2.Append(item)
}
@@ -153,7 +153,7 @@ func (m *Menu) insertBeforeRandom(_ *menu.CallbackData) {
m.anotherDynamicMenuCounter++
text := "Other Dynamic Menu Item " + strconv.Itoa(m.anotherDynamicMenuCounter)
newItem := menu.Text(text, text, nil, nil)
newItem := menu.Text(text, nil, nil)
m.dynamicMenuItems[text] = newItem
m.runtime.Log.Info(fmt.Sprintf("Inserting menu item '%s' before menu item '%s'", newItem.Label, randomMenuItem.Label))
@@ -176,7 +176,7 @@ func (m *Menu) insertAfterRandom(_ *menu.CallbackData) {
m.anotherDynamicMenuCounter++
text := "Other Dynamic Menu Item " + strconv.Itoa(m.anotherDynamicMenuCounter)
newItem := menu.Text(text, text, nil, nil)
newItem := menu.Text(text, nil, nil)
m.dynamicMenuItems[text] = newItem
m.runtime.Log.Info(fmt.Sprintf("Inserting menu item '%s' after menu item '%s'", newItem.Label, randomMenuItem.Label))
@@ -192,8 +192,8 @@ func (m *Menu) processPlainText(callbackData *menu.CallbackData) {
func (m *Menu) createApplicationMenu() *menu.Menu {
m.dynamicMenuOneSubmenu = menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", menu.NewMenuFromItems(
menu.Text("Add Menu Item", "Add Menu Item", keys.CmdOrCtrl("+"), m.addDynamicMenusOneMenu),
m.dynamicMenuOneSubmenu = menu.SubMenuWithID("Dynamic Menus 1", menu.NewMenuFromItems(
menu.Text("Add Menu Item", keys.CmdOrCtrl("+"), m.addDynamicMenusOneMenu),
menu.Separator(),
))
@@ -216,58 +216,58 @@ func (m *Menu) createApplicationMenu() *menu.Menu {
menu.Front(),
menu.SubMenu("Test Submenu", menu.NewMenuFromItems(
menu.Text("Plain text", "plain text", nil, m.processPlainText),
menu.Text("Show Dynamic Menus 2 Submenu", "show-dynamic-menus-2", nil, m.createDynamicMenuTwo),
menu.Text("Plain text", nil, m.processPlainText),
menu.Text("Show Dynamic Menus 2 Submenu", nil, m.createDynamicMenuTwo),
menu.SubMenu("Accelerators", menu.NewMenuFromItems(
menu.SubMenu("Modifiers", menu.NewMenuFromItems(
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.Text("Shift accelerator", keys.Shift("o"), nil),
menu.Text("Control accelerator", keys.Control("o"), nil),
menu.Text("Command accelerator", keys.CmdOrCtrl("o"), nil),
menu.Text("Option accelerator", keys.OptionOrAlt("o"), nil),
menu.Text("Combo accelerator", keys.Combo("o", keys.CmdOrCtrlKey, keys.ShiftKey), nil),
)),
menu.SubMenu("System Keys", menu.NewMenuFromItems(
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.Text("Backspace", keys.Key("Backspace"), nil),
menu.Text("Tab", keys.Key("Tab"), nil),
menu.Text("Return", keys.Key("Return"), nil),
menu.Text("Escape", keys.Key("Escape"), nil),
menu.Text("Left", keys.Key("Left"), nil),
menu.Text("Right", keys.Key("Right"), nil),
menu.Text("Up", keys.Key("Up"), nil),
menu.Text("Down", keys.Key("Down"), nil),
menu.Text("Space", keys.Key("Space"), nil),
menu.Text("Delete", keys.Key("Delete"), nil),
menu.Text("Home", keys.Key("Home"), nil),
menu.Text("End", keys.Key("End"), nil),
menu.Text("Page Up", keys.Key("Page Up"), nil),
menu.Text("Page Down", keys.Key("Page Down"), nil),
menu.Text("NumLock", keys.Key("NumLock"), nil),
)),
menu.SubMenu("Function Keys", menu.NewMenuFromItems(
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.Text("F1", keys.Key("F1"), nil),
menu.Text("F2", keys.Key("F2"), nil),
menu.Text("F3", keys.Key("F3"), nil),
menu.Text("F4", keys.Key("F4"), nil),
menu.Text("F5", keys.Key("F5"), nil),
menu.Text("F6", keys.Key("F6"), nil),
menu.Text("F7", keys.Key("F7"), nil),
menu.Text("F8", keys.Key("F8"), nil),
menu.Text("F9", keys.Key("F9"), nil),
menu.Text("F10", keys.Key("F10"), nil),
menu.Text("F11", keys.Key("F11"), nil),
menu.Text("F12", keys.Key("F12"), nil),
menu.Text("F13", keys.Key("F13"), nil),
menu.Text("F14", keys.Key("F14"), nil),
menu.Text("F15", keys.Key("F15"), nil),
menu.Text("F16", keys.Key("F16"), nil),
menu.Text("F17", keys.Key("F17"), nil),
menu.Text("F18", keys.Key("F18"), nil),
menu.Text("F19", keys.Key("F19"), nil),
menu.Text("F20", keys.Key("F20"), nil),
)),
menu.SubMenu("Standard Keys", menu.NewMenuFromItems(
menu.Text("Backtick", "Backtick", keys.Key("`"), nil),
menu.Text("Plus", "Plus", keys.Key("+"), nil),
menu.Text("Backtick", keys.Key("`"), nil),
menu.Text("Plus", keys.Key("+"), nil),
)),
)),
m.dynamicMenuOneSubmenu,
@@ -283,7 +283,6 @@ func (m *Menu) createApplicationMenu() *menu.Menu {
Hidden: true,
},
&menu.MenuItem{
ID: "checkbox-menu 1",
Label: "Checkbox Menu 1",
Type: menu.CheckboxType,
Accelerator: keys.CmdOrCtrl("l"),
@@ -293,11 +292,11 @@ func (m *Menu) createApplicationMenu() *menu.Menu {
fmt.Printf("It is now %v\n", data.MenuItem.Checked)
},
},
menu.Checkbox("Checkbox Menu 2", "checkbox-menu 2", false, nil, nil),
menu.Checkbox("Checkbox Menu 2", false, nil, nil),
menu.Separator(),
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),
menu.Radio("😀 Option 1", true, nil, nil),
menu.Radio("😺 Option 2", false, nil, nil),
menu.Radio("❤️ Option 3", false, nil, nil),
)),
))

View File

@@ -127,10 +127,10 @@ func (t *Tray) createTrayMenus() []*menu.TrayMenu {
trayMenu := &menu.TrayMenu{}
trayMenu.Label = "Test Tray Label"
trayMenu.Menu = menu.NewMenuFromItems(
menu.Text("Show Window", "Show Window", nil, t.showWindow),
menu.Text("Hide Window", "Hide Window", nil, t.hideWindow),
menu.Text("Minimise Window", "Minimise Window", nil, t.minimiseWindow),
menu.Text("Unminimise Window", "Unminimise Window", nil, t.unminimiseWindow),
menu.Text("Show Window", nil, t.showWindow),
menu.Text("Hide Window", nil, t.hideWindow),
menu.Text("Minimise Window", nil, t.minimiseWindow),
menu.Text("Unminimise Window", nil, t.unminimiseWindow),
)
t.trayMenu = trayMenu
@@ -138,7 +138,7 @@ func (t *Tray) createTrayMenus() []*menu.TrayMenu {
secondTrayMenu.Label = "Another tray label"
secondTrayMenu.Icon = "svelte"
secondTrayMenu.Menu = menu.NewMenuFromItems(
menu.Text("Update Label", "Update Label", nil, func(_ *menu.CallbackData) {
menu.Text("Update Label", nil, func(_ *menu.CallbackData) {
// Lock because this method will be called in a goroutine
t.lock.Lock()
defer t.lock.Unlock()
@@ -149,10 +149,10 @@ func (t *Tray) createTrayMenus() []*menu.TrayMenu {
t.runtime.Menu.UpdateTrayMenu(t.secondTrayMenu)
}),
menu.SubMenu("Select Icon", menu.NewMenuFromItems(
menu.Text("Svelte", "Svelte", nil, t.SvelteIcon),
menu.Text("Light", "Light", nil, t.LightIcon),
menu.Text("Dark", "Dark", nil, t.DarkIcon),
menu.Text("None", "None", nil, t.NoIcon),
menu.Text("Svelte", nil, t.SvelteIcon),
menu.Text("Light", nil, t.LightIcon),
menu.Text("Dark", nil, t.DarkIcon),
menu.Text("None", nil, t.NoIcon),
)),
)
t.secondTrayMenu = secondTrayMenu