package application import ( "os" "sync" "sync/atomic" ) type menuItemType int const ( text menuItemType = iota separator checkbox radio submenu ) var menuItemID uintptr var menuItemMap = make(map[uint]*MenuItem) var menuItemMapLock sync.Mutex func addToMenuItemMap(menuItem *MenuItem) { menuItemMapLock.Lock() menuItemMap[menuItem.id] = menuItem menuItemMapLock.Unlock() } func getMenuItemByID(id uint) *MenuItem { menuItemMapLock.Lock() defer menuItemMapLock.Unlock() return menuItemMap[id] } type menuItemImpl interface { setTooltip(s string) setLabel(s string) setDisabled(disabled bool) setChecked(checked bool) setAccelerator(accelerator *accelerator) } type MenuItem struct { id uint label string tooltip string disabled bool checked bool submenu *Menu callback func(*Context) itemType menuItemType accelerator *accelerator role Role contextMenuData *ContextMenuData impl menuItemImpl radioGroupMembers []*MenuItem } func newMenuItem(label string) *MenuItem { result := &MenuItem{ id: uint(atomic.AddUintptr(&menuItemID, 1)), label: label, itemType: text, } addToMenuItemMap(result) return result } func newMenuItemSeperator() *MenuItem { result := &MenuItem{ itemType: separator, } return result } func newMenuItemCheckbox(label string, checked bool) *MenuItem { result := &MenuItem{ id: uint(atomic.AddUintptr(&menuItemID, 1)), label: label, checked: checked, itemType: checkbox, } addToMenuItemMap(result) return result } func newMenuItemRadio(label string, checked bool) *MenuItem { result := &MenuItem{ id: uint(atomic.AddUintptr(&menuItemID, 1)), label: label, checked: checked, itemType: radio, } addToMenuItemMap(result) return result } func newSubMenuItem(label string) *MenuItem { result := &MenuItem{ id: uint(atomic.AddUintptr(&menuItemID, 1)), label: label, itemType: submenu, submenu: &Menu{ label: label, }, } addToMenuItemMap(result) return result } func newRole(role Role) *MenuItem { switch role { case AppMenu: return newAppMenu() case EditMenu: return newEditMenu() case FileMenu: return newFileMenu() case ViewMenu: return newViewMenu() case ServicesMenu: return newServicesMenu() case SpeechMenu: return newSpeechMenu() case WindowMenu: return newWindowMenu() case HelpMenu: return newHelpMenu() case Hide: return newHideMenuItem() case HideOthers: return newHideOthersMenuItem() case UnHide: return newUnhideMenuItem() case Undo: return newUndoMenuItem() case Redo: return newRedoMenuItem() case Cut: return newCutMenuItem() case Copy: return newCopyMenuItem() case Paste: return newPasteMenuItem() case PasteAndMatchStyle: return newPasteAndMatchStyleMenuItem() case SelectAll: return newSelectAllMenuItem() case Delete: return newDeleteMenuItem() case Quit: return newQuitMenuItem() case Close: return newCloseMenuItem() case About: return newAboutMenuItem() case Reload: return newReloadMenuItem() case ForceReload: return newForceReloadMenuItem() case ToggleFullscreen: return newToggleFullscreenMenuItem() case ToggleDevTools: return newToggleDevToolsMenuItem() case ResetZoom: return newZoomResetMenuItem() case ZoomIn: return newZoomInMenuItem() case ZoomOut: return newZoomOutMenuItem() case Minimize: return newMinimizeMenuItem() case Zoom: return newZoomMenuItem() default: println("No support for role:", role) os.Exit(1) } return nil } func newServicesMenu() *MenuItem { serviceMenu := newSubMenuItem("Services") serviceMenu.role = ServicesMenu return serviceMenu } func (m *MenuItem) handleClick() { var ctx = newContext(). withClickedMenuItem(m). withContextMenuData(m.contextMenuData) if m.itemType == checkbox { m.checked = !m.checked ctx.withChecked(m.checked) m.impl.setChecked(m.checked) } if m.itemType == radio { for _, member := range m.radioGroupMembers { member.checked = false member.impl.setChecked(false) } m.checked = true ctx.withChecked(true) m.impl.setChecked(true) } if m.callback != nil { go m.callback(ctx) } } func (m *MenuItem) SetAccelerator(shortcut string) *MenuItem { accelerator, err := parseAccelerator(shortcut) if err != nil { println("ERROR: invalid accelerator:", err.Error()) return m } m.accelerator = accelerator if m.impl != nil { m.impl.setAccelerator(accelerator) } return m } func (m *MenuItem) SetTooltip(s string) *MenuItem { m.tooltip = s if m.impl != nil { m.impl.setTooltip(s) } return m } func (m *MenuItem) SetLabel(s string) *MenuItem { m.label = s if m.impl != nil { m.impl.setLabel(s) } return m } func (m *MenuItem) SetEnabled(enabled bool) *MenuItem { m.disabled = !enabled if m.impl != nil { m.impl.setDisabled(m.disabled) } return m } func (m *MenuItem) SetChecked(checked bool) *MenuItem { m.checked = checked if m.impl != nil { m.impl.setChecked(m.checked) } return m } func (m *MenuItem) Checked() bool { return m.checked } func (m *MenuItem) OnClick(f func(*Context)) *MenuItem { m.callback = f return m } func (m *MenuItem) Label() string { return m.label } func (m *MenuItem) Tooltip() string { return m.tooltip } func (m *MenuItem) Enabled() bool { return !m.disabled } func (m *MenuItem) setContextData(data *ContextMenuData) { m.contextMenuData = data if m.submenu != nil { m.submenu.setContextData(data) } }