gui/pkg/systray/menu.go
Virgil 274a81689c
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
chore(gui): add AX usage examples
2026-04-02 20:36:02 +00:00

93 lines
2.4 KiB
Go

// pkg/systray/menu.go
package systray
import "forge.lthn.ai/core/go/pkg/core"
// SetMenu sets a dynamic menu on the tray from TrayMenuItem descriptors.
// Use: _ = m.SetMenu([]TrayMenuItem{{Label: "Quit", ActionID: "quit"}})
func (m *Manager) SetMenu(items []TrayMenuItem) error {
if m.tray == nil {
return core.E("systray.SetMenu", "tray not initialised", nil)
}
m.menuItems = append([]TrayMenuItem(nil), items...)
menu := m.buildMenu(items)
m.tray.SetMenu(menu)
return nil
}
// buildMenu recursively builds a PlatformMenu from TrayMenuItem descriptors.
func (m *Manager) buildMenu(items []TrayMenuItem) PlatformMenu {
menu := m.platform.NewMenu()
m.buildMenuInto(menu, items)
return menu
}
func (m *Manager) buildMenuInto(menu PlatformMenu, items []TrayMenuItem) {
for _, item := range items {
if item.Type == "separator" {
menu.AddSeparator()
continue
}
if len(item.Submenu) > 0 {
sub := menu.AddSubmenu(item.Label)
m.buildMenuInto(sub, item.Submenu)
continue
}
mi := menu.Add(item.Label)
if item.Tooltip != "" {
mi.SetTooltip(item.Tooltip)
}
if item.Disabled {
mi.SetEnabled(false)
}
if item.Checked {
mi.SetChecked(true)
}
if item.ActionID != "" {
actionID := item.ActionID
mi.OnClick(func() {
if cb, ok := m.GetCallback(actionID); ok {
cb()
}
})
}
}
}
// RegisterCallback registers a callback for a menu action ID.
// Use: m.RegisterCallback("quit", func() { _ = app.Quit() })
func (m *Manager) RegisterCallback(actionID string, callback func()) {
m.mu.Lock()
m.callbacks[actionID] = callback
m.mu.Unlock()
}
// UnregisterCallback removes a callback.
// Use: m.UnregisterCallback("quit")
func (m *Manager) UnregisterCallback(actionID string) {
m.mu.Lock()
delete(m.callbacks, actionID)
m.mu.Unlock()
}
// GetCallback returns the callback for an action ID.
// Use: callback, ok := m.GetCallback("quit")
func (m *Manager) GetCallback(actionID string) (func(), bool) {
m.mu.RLock()
defer m.mu.RUnlock()
cb, ok := m.callbacks[actionID]
return cb, ok
}
// GetInfo returns tray status information.
// Use: info := m.GetInfo()
func (m *Manager) GetInfo() map[string]any {
return map[string]any{
"active": m.IsActive(),
"tooltip": m.tooltip,
"label": m.label,
"hasIcon": m.hasIcon,
"hasTemplateIcon": m.hasTemplateIcon,
"menuItems": append([]TrayMenuItem(nil), m.menuItems...),
}
}