mirror of
https://github.com/atom/atom.git
synced 2026-01-22 13:28:01 -05:00
Rewrite based on feedback
This commit is contained in:
@@ -129,6 +129,7 @@ class AtomApplication
|
||||
@on 'application:minimize', -> Menu.sendActionToFirstResponder('performMiniaturize:')
|
||||
@on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:')
|
||||
@on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:')
|
||||
@on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y)
|
||||
|
||||
app.on 'will-quit', =>
|
||||
fs.unlinkSync socketPath if fs.existsSync(socketPath) # Clean the socket file when quit normally.
|
||||
|
||||
@@ -115,7 +115,8 @@ class AtomWindow
|
||||
@sendNativeCommand(command)
|
||||
|
||||
sendAtomCommand: (command, args...) ->
|
||||
ipc.sendChannel @browserWindow.getProcessId(), @browserWindow.getRoutingId(), 'command', command, args...
|
||||
action = if args[0]?.contextCommand then 'context-command' else 'command'
|
||||
ipc.sendChannel @browserWindow.getProcessId(), @browserWindow.getRoutingId(), action, command, args...
|
||||
|
||||
sendNativeCommand: (command) ->
|
||||
switch command
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
remote = require 'remote'
|
||||
|
||||
# Public: Provides a registry for commands that you'd like to appear in the
|
||||
# context menu.
|
||||
@@ -8,40 +10,74 @@ module.exports =
|
||||
class ContextMenuManager
|
||||
# Private:
|
||||
constructor: ->
|
||||
@mappings = {}
|
||||
@devModeMappings = {}
|
||||
@definitions = {}
|
||||
@devModeDefinitions = {}
|
||||
@activeElement = null
|
||||
|
||||
@devModeDefinitions['#root-view'] = [{ type: 'separator' }]
|
||||
@devModeDefinitions['#root-view'].push
|
||||
label: 'Inspect Element'
|
||||
command: 'application:inspect'
|
||||
executeAtBuild: (e) ->
|
||||
@.commandOptions = x: e.pageX, y: e.pageY
|
||||
|
||||
# Public: Registers a command to be displayed when the relevant item is right
|
||||
# clicked.
|
||||
#
|
||||
# * selector: The css selector for the active element which should include
|
||||
# the given command in its context menu.
|
||||
# * label: The text that should appear in the context menu.
|
||||
# * command: The command string that should be triggered on the activeElement
|
||||
# which matches your selector.
|
||||
# * definition: The object containing keys which match the menu template API.
|
||||
# * options:
|
||||
# + devMode: Indicates whether this command should only appear while the
|
||||
# editor is in dev mode.
|
||||
add: (selector, label, command, {devMode}={}) ->
|
||||
mappings = if devMode then @devModeMappings else @mappings
|
||||
mappings[selector] ?= []
|
||||
mappings[selector].push({label, command})
|
||||
add: (selector, definition, {devMode}={}) ->
|
||||
definitions = if devMode then @devModeDefinitions else @definitions
|
||||
(definitions[selector] ?= []).push(definition)
|
||||
|
||||
# Private:
|
||||
bindingsForElement: (element, {devMode}={}) ->
|
||||
mappings = if devMode then @devModeMappings else @mappings
|
||||
items for selector, items of mappings when element.webkitMatchesSelector(selector)
|
||||
# Private: Returns definitions which match the element and devMode.
|
||||
definitionsForElement: (element, {devMode}={}) ->
|
||||
definitions = if devMode then @devModeDefinitions else @definitions
|
||||
matchedDefinitions = []
|
||||
for selector, items of definitions when element.webkitMatchesSelector(selector)
|
||||
matchedDefinitions.push(_.clone(item)) for item in items
|
||||
|
||||
# Public: Used to generate the context menu for a specific element.
|
||||
matchedDefinitions
|
||||
|
||||
# Private: Used to generate the context menu for a specific element and it's
|
||||
# parents.
|
||||
#
|
||||
# The menu items are sorted such that menu items that match closest to the
|
||||
# active element are listed first. The further down the list you go, the higher
|
||||
# up the ancestor hierarchy they match.
|
||||
#
|
||||
# * element: The DOM element to generate the menu template for.
|
||||
menuTemplateForElement: (element) ->
|
||||
menuTemplate = []
|
||||
for devMode in [false, true]
|
||||
for items in @bindingsForElement(element, {devMode})
|
||||
for {label, command} in items
|
||||
template = {label}
|
||||
template.click = -> $(element).trigger(command)
|
||||
menuTemplate.push(template)
|
||||
menuTemplateForMostSpecificElement: (element, {devMode}={}) ->
|
||||
menuTemplate = @definitionsForElement(element, {devMode})
|
||||
if element.parentElement
|
||||
menuTemplate.concat(@menuTemplateForMostSpecificElement(element.parentElement, {devMode}))
|
||||
else
|
||||
menuTemplate
|
||||
|
||||
menuTemplate
|
||||
# Private: Returns a menu template for both normal entries as well as
|
||||
# development mode entries.
|
||||
combinedMenuTemplateForElement: (element) ->
|
||||
menuTemplate = @menuTemplateForMostSpecificElement(element)
|
||||
menuTemplate.concat(@menuTemplateForMostSpecificElement(element, devMode: true))
|
||||
|
||||
# Private: Executes `executeAtBuild` if defined for each menu item with
|
||||
# the provided event and then removes the `executeAtBuild` property from
|
||||
# the menu item.
|
||||
#
|
||||
# This is useful for commands that need to provide data about the event
|
||||
# to the command.
|
||||
executeBuildHandlers: (event, menuTemplate) ->
|
||||
for template in menuTemplate
|
||||
template?.executeAtBuild?.call(template, event)
|
||||
delete template.executeAtBuild
|
||||
|
||||
# Public: Request a context menu to be displayed.
|
||||
showForEvent: (event) ->
|
||||
@activeElement = event.target
|
||||
menuTemplate = @combinedMenuTemplateForElement(event.target)
|
||||
@executeBuildHandlers(event, menuTemplate)
|
||||
remote.getCurrentWindow().emit('context-menu', menuTemplate)
|
||||
|
||||
@@ -4,5 +4,17 @@ BrowserWindow = require 'browser-window'
|
||||
module.exports =
|
||||
class ContextMenu
|
||||
constructor: (template) ->
|
||||
menu = Menu.buildFromTemplate template
|
||||
template = @createClickHandlers(template)
|
||||
menu = Menu.buildFromTemplate(template)
|
||||
menu.popup(BrowserWindow.getFocusedWindow())
|
||||
|
||||
# Private: It's necessary to build the event handlers in this process, otherwise
|
||||
# closures are drug across processes and failed to be garbage collected
|
||||
# appropriately.
|
||||
createClickHandlers: (template) ->
|
||||
for item in template
|
||||
if item.command
|
||||
(item.commandOptions ?= {}).contextCommand = true
|
||||
item.click = do (item) ->
|
||||
=> global.atomApplication.sendCommand(item.command, item.commandOptions)
|
||||
item
|
||||
|
||||
@@ -14,6 +14,9 @@ class WindowEventHandler
|
||||
@subscribe ipc, 'command', (command, args...) ->
|
||||
$(window).trigger(command, args...)
|
||||
|
||||
@subscribe ipc, 'context-command', (command, args...) ->
|
||||
$(atom.contextMenu.activeElement).trigger(command, args...)
|
||||
|
||||
@subscribe $(window), 'focus', -> $("body").removeClass('is-blurred')
|
||||
@subscribe $(window), 'blur', -> $("body").addClass('is-blurred')
|
||||
@subscribe $(window), 'window:open-path', (event, {pathToOpen, initialLine}) ->
|
||||
@@ -39,14 +42,7 @@ class WindowEventHandler
|
||||
|
||||
@subscribe $(document), 'contextmenu', (e) ->
|
||||
e.preventDefault()
|
||||
menuTemplate = atom.contextMenu.menuTemplateForElement(e.target)
|
||||
|
||||
# FIXME: This should be registered as a dev binding on
|
||||
# atom.contextMenu, but I'm not sure where in the source.
|
||||
menuTemplate.push({ type: 'separator' })
|
||||
menuTemplate.push({ label: 'Inspect Element', click: -> remote.getCurrentWindow().inspectElement(e.pageX, e.pageY) })
|
||||
|
||||
remote.getCurrentWindow().emit('context-menu', menuTemplate)
|
||||
atom.contextMenu.showForEvent(e)
|
||||
|
||||
openLink: (event) =>
|
||||
location = $(event.target).attr('href')
|
||||
|
||||
Reference in New Issue
Block a user