nextCommandId = 0
-overrideProperty = (item, name, defaultValue) ->
- item[name] ?= defaultValue
-
- return unless process.platform is 'win32'
- v8Util.setHiddenValue item, name, item[name]
- Object.defineProperty item, name,
- enumerable: true
- get: -> v8Util.getHiddenValue item, name
- set: (val) ->
- v8Util.setHiddenValue item, name, val
- item.menu?._updateStates()
-
-overrideReadOnlyProperty = (item, name, defaultValue) ->
- item[name] ?= defaultValue
- Object.defineProperty item, name,
- enumerable: true
- writable: false
- value: item[name]
-
class MenuItem
@types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
@type = 'submenu' if not @type? and @submenu?
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
- overrideReadOnlyProperty this, 'type', 'normal'
- overrideReadOnlyProperty this, 'accelerator', null
- overrideReadOnlyProperty this, 'submenu', null
- overrideProperty this, 'label', ''
- overrideProperty this, 'sublabel', ''
- overrideProperty this, 'enabled', true
- overrideProperty this, 'visible', true
- overrideProperty this, 'checked', false
+ @overrideReadOnlyProperty 'type', 'normal'
+ @overrideReadOnlyProperty 'accelerator'
+ @overrideReadOnlyProperty 'submenu'
+ @overrideProperty 'label', ''
+ @overrideProperty 'sublabel', ''
+ @overrideProperty 'enabled', true
+ @overrideProperty 'visible', true
+ @overrideProperty 'checked', false
throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1
@commandId = ++nextCommandId
@click = =>
+ # Manually flip the checked flags when clicked.
+ @checked = !@checked if @type in ['checkbox', 'radio']
+
if typeof click is 'function'
click this, BrowserWindow.getFocusedWindow()
else if typeof @selector is 'string'
Menu.sendActionToFirstResponder @selector
+ overrideProperty: (name, defaultValue=null) ->
+ this[name] ?= defaultValue
+
+ # Update states when property is changed on Windows.
+ return unless process.platform is 'win32'
+ v8Util.setHiddenValue this, name, this[name]
+ Object.defineProperty this, name,
+ enumerable: true
+ get: => v8Util.getHiddenValue this, name
+ set: (val) =>
+ v8Util.setHiddenValue this, name, val
+ @menu?._updateStates()
+
+ overrideReadOnlyProperty: (name, defaultValue=null) ->
+ this[name] ?= defaultValue
+ Object.defineProperty this, name,
+ enumerable: true
+ writable: false
+ value: this[name]
+
module.exports = MenuItem
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?
- if activeItem.type in ['checkbox', 'radio']
- activeItem.checked = !activeItem.checked
- activeItem.click()
+ executeCommand: (commandId) => @commandsMap[commandId]?.click()
menuWillShow: =>
# Make sure radio groups have at least one menu item seleted.
for id, group of @groupsMap
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
when 'radio'
# Grouping radio menu items.
- item.groupId = generateGroupId(@items, pos)
+ item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
@groupsMap[item.groupId] ?= []
@groupsMap[item.groupId].push item
enumerable: true
get: -> v8Util.getHiddenValue item, 'checked'
set: (val) =>
- for otherItem in @groupsMap[item.groupId]
+ for otherItem in @groupsMap[item.groupId] when otherItem isnt item
v8Util.setHiddenValue otherItem, 'checked', false
v8Util.setHiddenValue item, 'checked', true
@setSublabel pos, item.sublabel if item.sublabel?
# Make menu accessable to items.
- item.menu = this
+ item.overrideReadOnlyProperty 'menu', this
# Remember the items.
@items.splice pos, 0, item