mirror of
https://github.com/atom/atom.git
synced 2026-01-22 21:38:10 -05:00
Add accelerator indicators to context menus
Electron allows us to pass an "accelerator" property for each menu item, which is renders to the right of the menu item. We were already adding these for the application level menus. This pull request adds the accelerator property to regular context menu items, which should make it easier for people to discover/recall key mappings for actions which they usually take via a context menu.
This commit is contained in:
@@ -231,3 +231,75 @@ describe "ContextMenuManager", ->
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
describe "::templateForEvent(target)", ->
|
||||
[keymaps, item] = []
|
||||
|
||||
beforeEach ->
|
||||
keymaps = atom.keymaps.add('source', {
|
||||
'.child': {
|
||||
'ctrl-a': 'test:my-command',
|
||||
'shift-b': 'test:my-other-command'
|
||||
}
|
||||
})
|
||||
item = {
|
||||
label: 'My Command',
|
||||
command: 'test:my-command',
|
||||
submenu: [
|
||||
{
|
||||
label: 'My Other Command',
|
||||
command: 'test:my-other-command',
|
||||
}
|
||||
]
|
||||
}
|
||||
contextMenu.add('.parent': [item])
|
||||
|
||||
afterEach ->
|
||||
keymaps.dispose()
|
||||
|
||||
|
||||
it "adds Electron-style accelerators to items that have keybindings", ->
|
||||
dispatchedEvent = {target: child}
|
||||
expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual(
|
||||
[
|
||||
label: 'My Command',
|
||||
command: 'test:my-command',
|
||||
accelerator: 'Ctrl+A',
|
||||
submenu: [
|
||||
{
|
||||
label: 'My Other Command',
|
||||
command: 'test:my-other-command',
|
||||
accelerator: 'Shift+B',
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
it "adds accelerators when a parent node has key bindings for a given command", ->
|
||||
dispatchedEvent = {target: grandchild}
|
||||
expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual(
|
||||
[
|
||||
label: 'My Command',
|
||||
command: 'test:my-command',
|
||||
accelerator: 'Ctrl+A',
|
||||
submenu: [
|
||||
{
|
||||
label: 'My Other Command',
|
||||
command: 'test:my-other-command',
|
||||
accelerator: 'Shift+B',
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
it "does not add accelerators when a child node has key bindings for a given command", ->
|
||||
dispatchedEvent = {target: parent}
|
||||
expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual(
|
||||
[
|
||||
label: 'My Command',
|
||||
command: 'test:my-command',
|
||||
submenu: [
|
||||
{
|
||||
label: 'My Other Command',
|
||||
command: 'test:my-other-command',
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
@@ -147,9 +147,20 @@ class ContextMenuManager
|
||||
currentTarget = currentTarget.parentElement
|
||||
|
||||
@pruneRedundantSeparators(template)
|
||||
@addAccelerators(template, event.target)
|
||||
|
||||
template
|
||||
|
||||
# Adds an `accelerator` property to items that have key bindings. Electron
|
||||
# uses this property to surface the relevant keymaps in the context menu.
|
||||
addAccelerators: (template, target) ->
|
||||
for id, item of template
|
||||
keymaps = @keymapManager.findKeyBindings({command: item.command, target})
|
||||
accelerator = MenuHelpers.acceleratorForKeystroke(keymaps?[0]?.keystrokes)
|
||||
item.accelerator = accelerator if accelerator
|
||||
if Array.isArray(item.submenu)
|
||||
@addAccelerators(item.submenu, target)
|
||||
|
||||
pruneRedundantSeparators: (menu) ->
|
||||
keepNextItemIfSeparator = false
|
||||
index = 0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{app, Menu} = require 'electron'
|
||||
_ = require 'underscore-plus'
|
||||
MenuHelpers = require '../menu-helpers'
|
||||
|
||||
# Used to manage the global application menu.
|
||||
#
|
||||
@@ -154,19 +155,7 @@ class ApplicationMenu
|
||||
# are Arrays containing the keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by atom shell to provide nice icons where available.
|
||||
# by Electron to provide nice icons where available.
|
||||
acceleratorForCommand: (command, keystrokesByCommand) ->
|
||||
firstKeystroke = keystrokesByCommand[command]?[0]
|
||||
return null unless firstKeystroke
|
||||
|
||||
modifiers = firstKeystroke.split(/-(?=.)/)
|
||||
key = modifiers.pop().toUpperCase().replace('+', 'Plus')
|
||||
|
||||
modifiers = modifiers.map (modifier) ->
|
||||
modifier.replace(/shift/ig, "Shift")
|
||||
.replace(/cmd/ig, "Command")
|
||||
.replace(/ctrl/ig, "Ctrl")
|
||||
.replace(/alt/ig, "Alt")
|
||||
|
||||
keys = modifiers.concat([key])
|
||||
keys.join("+")
|
||||
MenuHelpers.acceleratorForKeystroke(firstKeystroke)
|
||||
|
||||
@@ -46,9 +46,29 @@ normalizeLabel = (label) ->
|
||||
label.replace(/\&/g, '')
|
||||
|
||||
cloneMenuItem = (item) ->
|
||||
item = _.pick(item, 'type', 'label', 'enabled', 'visible', 'command', 'submenu', 'commandDetail', 'role')
|
||||
item = _.pick(item, 'type', 'label', 'enabled', 'visible', 'command', 'submenu', 'commandDetail', 'role', 'accelerator')
|
||||
if item.submenu?
|
||||
item.submenu = item.submenu.map (submenuItem) -> cloneMenuItem(submenuItem)
|
||||
item
|
||||
|
||||
module.exports = {merge, unmerge, normalizeLabel, cloneMenuItem}
|
||||
# Determine the Electron accelerator for a given Atom keystroke.
|
||||
#
|
||||
# keystroke - The keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by Electron to provide nice icons where available.
|
||||
acceleratorForKeystroke = (keystroke) ->
|
||||
return null unless keystroke
|
||||
modifiers = keystroke.split(/-(?=.)/)
|
||||
key = modifiers.pop().toUpperCase().replace('+', 'Plus')
|
||||
|
||||
modifiers = modifiers.map (modifier) ->
|
||||
modifier.replace(/shift/ig, "Shift")
|
||||
.replace(/cmd/ig, "Command")
|
||||
.replace(/ctrl/ig, "Ctrl")
|
||||
.replace(/alt/ig, "Alt")
|
||||
|
||||
keys = modifiers.concat([key])
|
||||
keys.join("+")
|
||||
|
||||
module.exports = {merge, unmerge, normalizeLabel, cloneMenuItem, acceleratorForKeystroke}
|
||||
|
||||
Reference in New Issue
Block a user