mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
Support context menu data
Support StartHidden
This commit is contained in:
@@ -25,7 +25,7 @@ type App struct {
|
||||
}
|
||||
|
||||
// CreateApp returns a null application
|
||||
func CreateApp(options *options.App) *App {
|
||||
func CreateApp(_ *options.App) *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,5 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
// Bind the dummy interface
|
||||
func (a *App) Bind(dummy interface{}) error {
|
||||
return nil
|
||||
func (a *App) Bind(_ interface{}) {
|
||||
}
|
||||
|
||||
@@ -23,14 +23,15 @@ 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
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
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
|
||||
|
||||
// Indicates if the app is in debug mode
|
||||
debug bool
|
||||
@@ -94,8 +95,9 @@ func (a *App) Run() error {
|
||||
// Start the runtime
|
||||
applicationMenu := options.GetApplicationMenu(a.options)
|
||||
trayMenu := options.GetTrayMenu(a.options)
|
||||
contextMenus := options.GetContextMenus(a.options)
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(a.servicebus, a.logger, applicationMenu, trayMenu)
|
||||
runtimesubsystem, err := subsystem.NewRuntime(a.servicebus, a.logger, applicationMenu, trayMenu, contextMenus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -179,6 +181,19 @@ func (a *App) Run() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -34,4 +34,6 @@ extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *fi
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
extern void UpdateMenu(void *app, char *menuAsJSON);
|
||||
extern void UpdateTray(void *app, char *menuAsJSON);
|
||||
extern void UpdateContextMenus(void *app, char *contextMenusAsJSON);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@ import "C"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"strconv"
|
||||
|
||||
@@ -188,3 +189,19 @@ func (c *Client) UpdateTray(menu *menu.Menu) {
|
||||
}
|
||||
C.UpdateTray(c.app.app, c.app.string2CString(string(trayMenuJSON)))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateContextMenus(contextMenus *menu.ContextMenus) {
|
||||
|
||||
// Guard against nil contextMenus
|
||||
if contextMenus == nil {
|
||||
return
|
||||
}
|
||||
// Process the menu
|
||||
contextMenusJSON, err := json.Marshal(contextMenus)
|
||||
fmt.Printf("\n\nUPDATED CONTEXT MENUS:\n %+v\n\n", string(contextMenusJSON))
|
||||
if err != nil {
|
||||
c.app.logger.Error("Error processing updated Context Menus: %s", err.Error())
|
||||
return
|
||||
}
|
||||
C.UpdateContextMenus(c.app.app, c.app.string2CString(string(contextMenusJSON)))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ extern void SetContextMenus(void *, const char *);
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
@@ -116,6 +117,7 @@ func (a *Application) processPlatformSettings() error {
|
||||
contextMenus := options.GetContextMenus(a.config)
|
||||
if contextMenus != nil {
|
||||
contextMenusJSON, err := json.Marshal(contextMenus)
|
||||
fmt.Printf("\n\nCONTEXT MENUS:\n %+v\n\n", string(contextMenusJSON))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type Client interface {
|
||||
DarkModeEnabled(callbackID string)
|
||||
UpdateMenu(menu *menu.Menu)
|
||||
UpdateTray(menu *menu.Menu)
|
||||
UpdateContextMenus(contextMenus *menu.ContextMenus)
|
||||
}
|
||||
|
||||
// DispatchClient is what the frontends use to interface with the
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
// ContextMenusOnMessage is used to emit listener registration requests
|
||||
// on the service bus
|
||||
type ContextMenusOnMessage struct {
|
||||
// MenuID is the id of the menu item we are interested in
|
||||
MenuID string
|
||||
// Callback is called when the menu is clicked
|
||||
Callback func(*menu.MenuItem, string)
|
||||
}
|
||||
|
||||
// contextMenusMessageParser does what it says on the tin!
|
||||
func contextMenusMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Menu messages must be at least 2 bytes
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("context menus message was an invalid length")
|
||||
}
|
||||
|
||||
var topic string
|
||||
var data interface{}
|
||||
|
||||
// Switch the message type
|
||||
switch message[1] {
|
||||
case 'C':
|
||||
contextMenuData := message[2:]
|
||||
topic = "contextmenus:clicked"
|
||||
data = contextMenuData
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid menu message: %s", message)
|
||||
}
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: topic, Data: data}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -20,6 +20,7 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
||||
'S': systemMessageParser,
|
||||
'M': menuMessageParser,
|
||||
'T': trayMessageParser,
|
||||
'X': contextMenusMessageParser,
|
||||
}
|
||||
|
||||
// Parse will attempt to parse the given message
|
||||
|
||||
@@ -20,7 +20,7 @@ func trayMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Menu messages must be at least 2 bytes
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("event message was an invalid length")
|
||||
return nil, fmt.Errorf("tray message was an invalid length")
|
||||
}
|
||||
|
||||
var topic string
|
||||
|
||||
@@ -17,15 +17,16 @@ import (
|
||||
// Dispatcher translates messages received from the frontend
|
||||
// and publishes them onto the service bus
|
||||
type Dispatcher struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
resultChannel <-chan *servicebus.Message
|
||||
eventChannel <-chan *servicebus.Message
|
||||
windowChannel <-chan *servicebus.Message
|
||||
dialogChannel <-chan *servicebus.Message
|
||||
systemChannel <-chan *servicebus.Message
|
||||
menuChannel <-chan *servicebus.Message
|
||||
trayChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
quitChannel <-chan *servicebus.Message
|
||||
resultChannel <-chan *servicebus.Message
|
||||
eventChannel <-chan *servicebus.Message
|
||||
windowChannel <-chan *servicebus.Message
|
||||
dialogChannel <-chan *servicebus.Message
|
||||
systemChannel <-chan *servicebus.Message
|
||||
menuChannel <-chan *servicebus.Message
|
||||
contextMenuChannel <-chan *servicebus.Message
|
||||
trayChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
servicebus *servicebus.ServiceBus
|
||||
logger logger.CustomLogger
|
||||
@@ -77,23 +78,29 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextMenuChannel, err := servicebus.Subscribe("contextmenufrontend:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trayChannel, err := servicebus.Subscribe("trayfrontend:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &Dispatcher{
|
||||
servicebus: servicebus,
|
||||
eventChannel: eventChannel,
|
||||
logger: logger.CustomLogger("Message Dispatcher"),
|
||||
clients: make(map[string]*DispatchClient),
|
||||
resultChannel: resultChannel,
|
||||
quitChannel: quitChannel,
|
||||
windowChannel: windowChannel,
|
||||
dialogChannel: dialogChannel,
|
||||
systemChannel: systemChannel,
|
||||
menuChannel: menuChannel,
|
||||
trayChannel: trayChannel,
|
||||
servicebus: servicebus,
|
||||
eventChannel: eventChannel,
|
||||
logger: logger.CustomLogger("Message Dispatcher"),
|
||||
clients: make(map[string]*DispatchClient),
|
||||
resultChannel: resultChannel,
|
||||
quitChannel: quitChannel,
|
||||
windowChannel: windowChannel,
|
||||
dialogChannel: dialogChannel,
|
||||
systemChannel: systemChannel,
|
||||
menuChannel: menuChannel,
|
||||
trayChannel: trayChannel,
|
||||
contextMenuChannel: contextMenuChannel,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -125,6 +132,8 @@ func (d *Dispatcher) Start() error {
|
||||
d.processSystemMessage(systemMessage)
|
||||
case menuMessage := <-d.menuChannel:
|
||||
d.processMenuMessage(menuMessage)
|
||||
case contextMenuMessage := <-d.contextMenuChannel:
|
||||
d.processContextMenuMessage(contextMenuMessage)
|
||||
case trayMessage := <-d.trayChannel:
|
||||
d.processTrayMessage(trayMessage)
|
||||
}
|
||||
@@ -446,6 +455,34 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
||||
}
|
||||
}
|
||||
func (d *Dispatcher) processContextMenuMessage(result *servicebus.Message) {
|
||||
splitTopic := strings.Split(result.Topic(), ":")
|
||||
if len(splitTopic) < 2 {
|
||||
d.logger.Error("Invalid contextmenu message : %#v", result.Data())
|
||||
return
|
||||
}
|
||||
|
||||
command := splitTopic[1]
|
||||
switch command {
|
||||
case "update":
|
||||
|
||||
updatedContextMenus, ok := result.Data().(*menu.ContextMenus)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid data for 'contextmenufrontend: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.UpdateContextMenus(updatedContextMenus)
|
||||
}
|
||||
|
||||
default:
|
||||
d.logger.Error("Unknown contextmenufrontend command: %s", command)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) processTrayMessage(result *servicebus.Message) {
|
||||
splitTopic := strings.Split(result.Topic(), ":")
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
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)
|
||||
}
|
||||
@@ -57,10 +57,10 @@ export function Init() {
|
||||
e.preventDefault();
|
||||
}
|
||||
if( contextMenuId != null ) {
|
||||
let contextData = currentElement.dataset['wails-context-menu-data'];
|
||||
let message = {
|
||||
id: contextMenuId,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
data: contextData || "",
|
||||
};
|
||||
window.webkit.messageHandlers.contextMenu.postMessage(JSON.stringify(message));
|
||||
}
|
||||
|
||||
@@ -7,30 +7,32 @@ import (
|
||||
|
||||
// Runtime is a means for the user to interact with the application at runtime
|
||||
type Runtime struct {
|
||||
Browser Browser
|
||||
Events Events
|
||||
Window Window
|
||||
Dialog Dialog
|
||||
System System
|
||||
Menu Menu
|
||||
Tray Tray
|
||||
Store *StoreProvider
|
||||
Log Log
|
||||
bus *servicebus.ServiceBus
|
||||
Browser Browser
|
||||
Events Events
|
||||
Window Window
|
||||
Dialog Dialog
|
||||
System System
|
||||
Menu Menu
|
||||
ContextMenu ContextMenus
|
||||
Tray Tray
|
||||
Store *StoreProvider
|
||||
Log Log
|
||||
bus *servicebus.ServiceBus
|
||||
}
|
||||
|
||||
// New creates a new runtime
|
||||
func New(serviceBus *servicebus.ServiceBus, menu *menu.Menu, trayMenu *menu.Menu) *Runtime {
|
||||
func New(serviceBus *servicebus.ServiceBus, menu *menu.Menu, trayMenu *menu.Menu, contextMenus *menu.ContextMenus) *Runtime {
|
||||
result := &Runtime{
|
||||
Browser: newBrowser(),
|
||||
Events: newEvents(serviceBus),
|
||||
Window: newWindow(serviceBus),
|
||||
Dialog: newDialog(serviceBus),
|
||||
System: newSystem(serviceBus),
|
||||
Menu: newMenu(serviceBus, menu),
|
||||
Tray: newTray(serviceBus, trayMenu),
|
||||
Log: newLog(serviceBus),
|
||||
bus: serviceBus,
|
||||
Browser: newBrowser(),
|
||||
Events: newEvents(serviceBus),
|
||||
Window: newWindow(serviceBus),
|
||||
Dialog: newDialog(serviceBus),
|
||||
System: newSystem(serviceBus),
|
||||
Menu: newMenu(serviceBus, menu),
|
||||
Tray: newTray(serviceBus, trayMenu),
|
||||
ContextMenu: newContextMenus(serviceBus, contextMenus),
|
||||
Log: newLog(serviceBus),
|
||||
bus: serviceBus,
|
||||
}
|
||||
result.Store = newStore(result)
|
||||
return result
|
||||
|
||||
@@ -20,7 +20,7 @@ type trayRuntime struct {
|
||||
}
|
||||
|
||||
// newTray creates a new Menu struct
|
||||
func newTray(bus *servicebus.ServiceBus, menu *menu.Menu) Menu {
|
||||
func newTray(bus *servicebus.ServiceBus, menu *menu.Menu) Tray {
|
||||
return &trayRuntime{
|
||||
bus: bus,
|
||||
trayMenu: menu,
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
@@ -24,7 +24,7 @@ type Runtime struct {
|
||||
}
|
||||
|
||||
// NewRuntime creates a new runtime subsystem
|
||||
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, menu *menu.Menu, trayMenu *menu.Menu) (*Runtime, error) {
|
||||
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, menu *menu.Menu, trayMenu *menu.Menu, contextMenus *menu.ContextMenus) (*Runtime, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
@@ -42,7 +42,7 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, menu *menu.Me
|
||||
quitChannel: quitChannel,
|
||||
runtimeChannel: runtimeChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: runtime.New(bus, menu, trayMenu),
|
||||
runtime: runtime.New(bus, menu, trayMenu, contextMenus),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package menu
|
||||
|
||||
type ContextMenus struct {
|
||||
Items map[string]*Menu
|
||||
}
|
||||
|
||||
func NewContextMenus() *ContextMenus {
|
||||
return &ContextMenus{
|
||||
Items: make(map[string]*Menu),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -10,5 +10,5 @@ type Options struct {
|
||||
WindowBackgroundIsTranslucent bool
|
||||
Menu *menu.Menu
|
||||
Tray *menu.Menu
|
||||
ContextMenus map[string]*menu.Menu
|
||||
ContextMenus *menu.ContextMenus
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type App struct {
|
||||
StartHidden bool
|
||||
DevTools bool
|
||||
RGBA int
|
||||
ContextMenus map[string]*menu.Menu
|
||||
ContextMenus *menu.ContextMenus
|
||||
Tray *menu.Menu
|
||||
Menu *menu.Menu
|
||||
Mac *mac.Options
|
||||
@@ -97,11 +97,11 @@ func GetApplicationMenu(appoptions *App) *menu.Menu {
|
||||
return result
|
||||
}
|
||||
|
||||
func GetContextMenus(appoptions *App) map[string]*menu.Menu {
|
||||
var result map[string]*menu.Menu
|
||||
func GetContextMenus(appoptions *App) *menu.ContextMenus {
|
||||
var result *menu.ContextMenus
|
||||
|
||||
result = appoptions.ContextMenus
|
||||
var contextMenuOverrides map[string]*menu.Menu
|
||||
var contextMenuOverrides *menu.ContextMenus
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
if appoptions.Mac != nil {
|
||||
@@ -118,8 +118,10 @@ func GetContextMenus(appoptions *App) map[string]*menu.Menu {
|
||||
}
|
||||
|
||||
// Overwrite defaults with OS Specific context menus
|
||||
for id, contextMenu := range contextMenuOverrides {
|
||||
result[id] = contextMenu
|
||||
if contextMenuOverrides != nil {
|
||||
for id, contextMenu := range contextMenuOverrides.Items {
|
||||
result.AddMenu(id, contextMenu)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user