electron/atom/browser/api/lib/menu.coffee
Cheng Zhao 04fbec5120 Generate menu group id automatically.
In GTK+ radio menu items are managed automatically, so group id won't
have any effect there, in the meanwhile we need to maintain the same
behavior on all platforms, so we have to generate group id instead of
letting users specifying it.
2014-05-25 12:37:00 +08:00

111 lines
3.5 KiB
CoffeeScript

BrowserWindow = require 'browser-window'
EventEmitter = require('events').EventEmitter
MenuItem = require 'menu-item'
bindings = process.atomBinding 'menu'
# Automatically generated radio menu item's group id.
nextGroupId = 0
# Search between seperators to find a radio menu item and return its group id,
# otherwise generate a group id.
generateGroupId = (items, pos) ->
if pos > 0
for i in [pos - 1..0]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
else if pos < items.length
for i in [pos..items.length - 1]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
++nextGroupId
Menu = bindings.Menu
Menu::__proto__ = EventEmitter.prototype
popup = Menu::popup
Menu::popup = (window) ->
throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow
popup.call this, window
Menu::append = (item) ->
@insert @getItemCount(), item
Menu::insert = (pos, item) ->
throw new TypeError('Invalid item') unless item?.constructor is MenuItem
# Create delegate.
unless @delegate?
@commandsMap = {}
@groupsMap = {}
@items = []
@delegate =
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
executeCommand: (commandId) =>
activeItem = @commandsMap[commandId]
# Manually flip the checked flags when clicked.
if activeItem?
switch activeItem.type
when 'checkbox'
activeItem.checked = !activeItem.checked
when 'radio'
for item in @groupsMap[activeItem.groupId]
item.checked = false
activeItem.checked = true
activeItem.click()
switch item.type
when 'normal' then @insertItem pos, item.commandId, item.label
when 'checkbox' then @insertCheckItem pos, item.commandId, item.label
when 'separator' then @insertSeparator pos
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
when 'radio'
# Grouping radio menu items.
item.groupId = generateGroupId(@items, pos)
@groupsMap[item.groupId] ?= []
@groupsMap[item.groupId].push item
@insertRadioItem pos, item.commandId, item.label, item.groupId
@setSublabel pos, item.sublabel if item.sublabel?
# Remember the items.
@items.splice pos, 0, item
@commandsMap[item.commandId] = item
applicationMenu = null
Menu.setApplicationMenu = (menu) ->
throw new TypeError('Invalid menu') unless menu?.constructor is Menu
applicationMenu = menu # Keep a reference.
if process.platform is 'darwin'
bindings.setApplicationMenu menu
else
windows = BrowserWindow.getAllWindows()
w.setMenu menu for w in windows
Menu.getApplicationMenu = -> applicationMenu
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
Menu.buildFromTemplate = (template) ->
throw new TypeError('Invalid template for Menu') unless Array.isArray template
menu = new Menu
for item in template
throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object'
item.submenu = Menu.buildFromTemplate item.submenu if item.submenu?
menuItem = new MenuItem(item)
menuItem[key] = value for key, value of item when not menuItem[key]?
menu.append menuItem
menu
module.exports = Menu