mirror of
https://github.com/atom/atom.git
synced 2026-02-13 08:04:56 -05:00
Decaffeinate ApplicationMenu
This commit is contained in:
@@ -1,161 +0,0 @@
|
||||
{app, Menu} = require 'electron'
|
||||
_ = require 'underscore-plus'
|
||||
MenuHelpers = require '../menu-helpers'
|
||||
|
||||
# Used to manage the global application menu.
|
||||
#
|
||||
# It's created by {AtomApplication} upon instantiation and used to add, remove
|
||||
# and maintain the state of all menu items.
|
||||
module.exports =
|
||||
class ApplicationMenu
|
||||
constructor: (@version, @autoUpdateManager) ->
|
||||
@windowTemplates = new WeakMap()
|
||||
@setActiveTemplate(@getDefaultTemplate())
|
||||
@autoUpdateManager.on 'state-changed', (state) => @showUpdateMenuItem(state)
|
||||
|
||||
# Public: Updates the entire menu with the given keybindings.
|
||||
#
|
||||
# window - The BrowserWindow this menu template is associated with.
|
||||
# template - The Object which describes the menu to display.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
update: (window, template, keystrokesByCommand) ->
|
||||
@translateTemplate(template, keystrokesByCommand)
|
||||
@substituteVersion(template)
|
||||
@windowTemplates.set(window, template)
|
||||
@setActiveTemplate(template) if window is @lastFocusedWindow
|
||||
|
||||
setActiveTemplate: (template) ->
|
||||
unless _.isEqual(template, @activeTemplate)
|
||||
@activeTemplate = template
|
||||
@menu = Menu.buildFromTemplate(_.deepClone(template))
|
||||
Menu.setApplicationMenu(@menu)
|
||||
|
||||
@showUpdateMenuItem(@autoUpdateManager.getState())
|
||||
|
||||
# Register a BrowserWindow with this application menu.
|
||||
addWindow: (window) ->
|
||||
@lastFocusedWindow ?= window
|
||||
|
||||
focusHandler = =>
|
||||
@lastFocusedWindow = window
|
||||
if template = @windowTemplates.get(window)
|
||||
@setActiveTemplate(template)
|
||||
|
||||
window.on 'focus', focusHandler
|
||||
window.once 'closed', =>
|
||||
@lastFocusedWindow = null if window is @lastFocusedWindow
|
||||
@windowTemplates.delete(window)
|
||||
window.removeListener 'focus', focusHandler
|
||||
|
||||
@enableWindowSpecificItems(true)
|
||||
|
||||
# Flattens the given menu and submenu items into an single Array.
|
||||
#
|
||||
# menu - A complete menu configuration object for atom-shell's menu API.
|
||||
#
|
||||
# Returns an Array of native menu items.
|
||||
flattenMenuItems: (menu) ->
|
||||
items = []
|
||||
for index, item of menu.items or {}
|
||||
items.push(item)
|
||||
items = items.concat(@flattenMenuItems(item.submenu)) if item.submenu
|
||||
items
|
||||
|
||||
# Flattens the given menu template into an single Array.
|
||||
#
|
||||
# template - An object describing the menu item.
|
||||
#
|
||||
# Returns an Array of native menu items.
|
||||
flattenMenuTemplate: (template) ->
|
||||
items = []
|
||||
for item in template
|
||||
items.push(item)
|
||||
items = items.concat(@flattenMenuTemplate(item.submenu)) if item.submenu
|
||||
items
|
||||
|
||||
# Public: Used to make all window related menu items are active.
|
||||
#
|
||||
# enable - If true enables all window specific items, if false disables all
|
||||
# window specific items.
|
||||
enableWindowSpecificItems: (enable) ->
|
||||
for item in @flattenMenuItems(@menu)
|
||||
item.enabled = enable if item.metadata?.windowSpecific
|
||||
return
|
||||
|
||||
# Replaces VERSION with the current version.
|
||||
substituteVersion: (template) ->
|
||||
if (item = _.find(@flattenMenuTemplate(template), ({label}) -> label is 'VERSION'))
|
||||
item.label = "Version #{@version}"
|
||||
|
||||
# Sets the proper visible state the update menu items
|
||||
showUpdateMenuItem: (state) ->
|
||||
checkForUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label is 'Check for Update')
|
||||
checkingForUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label is 'Checking for Update')
|
||||
downloadingUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label is 'Downloading Update')
|
||||
installUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label is 'Restart and Install Update')
|
||||
|
||||
return unless checkForUpdateItem? and checkingForUpdateItem? and downloadingUpdateItem? and installUpdateItem?
|
||||
|
||||
checkForUpdateItem.visible = false
|
||||
checkingForUpdateItem.visible = false
|
||||
downloadingUpdateItem.visible = false
|
||||
installUpdateItem.visible = false
|
||||
|
||||
switch state
|
||||
when 'idle', 'error', 'no-update-available'
|
||||
checkForUpdateItem.visible = true
|
||||
when 'checking'
|
||||
checkingForUpdateItem.visible = true
|
||||
when 'downloading'
|
||||
downloadingUpdateItem.visible = true
|
||||
when 'update-available'
|
||||
installUpdateItem.visible = true
|
||||
|
||||
# Default list of menu items.
|
||||
#
|
||||
# Returns an Array of menu item Objects.
|
||||
getDefaultTemplate: ->
|
||||
[
|
||||
label: "Atom"
|
||||
submenu: [
|
||||
{label: "Check for Update", metadata: {autoUpdate: true}}
|
||||
{label: 'Reload', accelerator: 'Command+R', click: => @focusedWindow()?.reload()}
|
||||
{label: 'Close Window', accelerator: 'Command+Shift+W', click: => @focusedWindow()?.close()}
|
||||
{label: 'Toggle Dev Tools', accelerator: 'Command+Alt+I', click: => @focusedWindow()?.toggleDevTools()}
|
||||
{label: 'Quit', accelerator: 'Command+Q', click: -> app.quit()}
|
||||
]
|
||||
]
|
||||
|
||||
focusedWindow: ->
|
||||
_.find global.atomApplication.getAllWindows(), (atomWindow) -> atomWindow.isFocused()
|
||||
|
||||
# Combines a menu template with the appropriate keystroke.
|
||||
#
|
||||
# template - An Object conforming to atom-shell's menu api but lacking
|
||||
# accelerator and click properties.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
#
|
||||
# Returns a complete menu configuration object for atom-shell's menu API.
|
||||
translateTemplate: (template, keystrokesByCommand) ->
|
||||
template.forEach (item) =>
|
||||
item.metadata ?= {}
|
||||
if item.command
|
||||
item.accelerator = @acceleratorForCommand(item.command, keystrokesByCommand)
|
||||
item.click = -> global.atomApplication.sendCommand(item.command, item.commandDetail)
|
||||
item.metadata.windowSpecific = true unless /^application:/.test(item.command, item.commandDetail)
|
||||
@translateTemplate(item.submenu, keystrokesByCommand) if item.submenu
|
||||
template
|
||||
|
||||
# Determine the accelerator for a given command.
|
||||
#
|
||||
# command - The name of the command.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by Electron to provide nice icons where available.
|
||||
acceleratorForCommand: (command, keystrokesByCommand) ->
|
||||
firstKeystroke = keystrokesByCommand[command]?[0]
|
||||
MenuHelpers.acceleratorForKeystroke(firstKeystroke)
|
||||
225
src/main-process/application-menu.js
Normal file
225
src/main-process/application-menu.js
Normal file
@@ -0,0 +1,225 @@
|
||||
const {app, Menu} = require('electron')
|
||||
const _ = require('underscore-plus')
|
||||
const MenuHelpers = require('../menu-helpers')
|
||||
|
||||
// Used to manage the global application menu.
|
||||
//
|
||||
// It's created by {AtomApplication} upon instantiation and used to add, remove
|
||||
// and maintain the state of all menu items.
|
||||
module.exports =
|
||||
class ApplicationMenu {
|
||||
constructor (version, autoUpdateManager) {
|
||||
this.version = version
|
||||
this.autoUpdateManager = autoUpdateManager
|
||||
this.windowTemplates = new WeakMap()
|
||||
this.setActiveTemplate(this.getDefaultTemplate())
|
||||
this.autoUpdateManager.on('state-changed', state => this.showUpdateMenuItem(state))
|
||||
}
|
||||
|
||||
// Public: Updates the entire menu with the given keybindings.
|
||||
//
|
||||
// window - The BrowserWindow this menu template is associated with.
|
||||
// template - The Object which describes the menu to display.
|
||||
// keystrokesByCommand - An Object where the keys are commands and the values
|
||||
// are Arrays containing the keystroke.
|
||||
update (window, template, keystrokesByCommand) {
|
||||
this.translateTemplate(template, keystrokesByCommand)
|
||||
this.substituteVersion(template)
|
||||
this.windowTemplates.set(window, template)
|
||||
if (window === this.lastFocusedWindow) return this.setActiveTemplate(template)
|
||||
}
|
||||
|
||||
setActiveTemplate (template) {
|
||||
if (!_.isEqual(template, this.activeTemplate)) {
|
||||
this.activeTemplate = template
|
||||
this.menu = Menu.buildFromTemplate(_.deepClone(template))
|
||||
Menu.setApplicationMenu(this.menu)
|
||||
}
|
||||
|
||||
return this.showUpdateMenuItem(this.autoUpdateManager.getState())
|
||||
}
|
||||
|
||||
// Register a BrowserWindow with this application menu.
|
||||
addWindow (window) {
|
||||
if (this.lastFocusedWindow == null) this.lastFocusedWindow = window
|
||||
|
||||
const focusHandler = () => {
|
||||
this.lastFocusedWindow = window
|
||||
const template = this.windowTemplates.get(window)
|
||||
if (template) this.setActiveTemplate(template)
|
||||
}
|
||||
|
||||
window.on('focus', focusHandler)
|
||||
window.once('closed', () => {
|
||||
if (window === this.lastFocusedWindow) this.lastFocusedWindow = null
|
||||
this.windowTemplates.delete(window)
|
||||
window.removeListener('focus', focusHandler)
|
||||
})
|
||||
|
||||
this.enableWindowSpecificItems(true)
|
||||
}
|
||||
|
||||
// Flattens the given menu and submenu items into an single Array.
|
||||
//
|
||||
// menu - A complete menu configuration object for atom-shell's menu API.
|
||||
//
|
||||
// Returns an Array of native menu items.
|
||||
flattenMenuItems (menu) {
|
||||
const object = menu.items || {}
|
||||
let items = []
|
||||
for (let index in object) {
|
||||
const item = object[index]
|
||||
items.push(item)
|
||||
if (item.submenu) items = items.concat(this.flattenMenuItems(item.submenu))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Flattens the given menu template into an single Array.
|
||||
//
|
||||
// template - An object describing the menu item.
|
||||
//
|
||||
// Returns an Array of native menu items.
|
||||
flattenMenuTemplate (template) {
|
||||
let items = []
|
||||
for (let item of template) {
|
||||
items.push(item)
|
||||
if (item.submenu) items = items.concat(this.flattenMenuTemplate(item.submenu))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Public: Used to make all window related menu items are active.
|
||||
//
|
||||
// enable - If true enables all window specific items, if false disables all
|
||||
// window specific items.
|
||||
enableWindowSpecificItems (enable) {
|
||||
for (let item of this.flattenMenuItems(this.menu)) {
|
||||
if (item.metadata && item.metadata.windowSpecific) item.enabled = enable
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces VERSION with the current version.
|
||||
substituteVersion (template) {
|
||||
let item = this.flattenMenuTemplate(template).find(({label}) => label === 'VERSION')
|
||||
if (item) item.label = `Version ${this.version}`
|
||||
}
|
||||
|
||||
// Sets the proper visible state the update menu items
|
||||
showUpdateMenuItem (state) {
|
||||
const items = this.flattenMenuItems(this.menu)
|
||||
const checkForUpdateItem = items.find(({label}) => label === 'Check for Update')
|
||||
const checkingForUpdateItem = items.find(({label}) => label === 'Checking for Update')
|
||||
const downloadingUpdateItem = items.find(({label}) => label === 'Downloading Update')
|
||||
const installUpdateItem = items.find(({label}) => label === 'Restart and Install Update')
|
||||
|
||||
if (!checkForUpdateItem || !checkingForUpdateItem ||
|
||||
!downloadingUpdateItem || !installUpdateItem) return
|
||||
|
||||
checkForUpdateItem.visible = false
|
||||
checkingForUpdateItem.visible = false
|
||||
downloadingUpdateItem.visible = false
|
||||
installUpdateItem.visible = false
|
||||
|
||||
switch (state) {
|
||||
case 'idle':
|
||||
case 'error':
|
||||
case 'no-update-available':
|
||||
checkForUpdateItem.visible = true
|
||||
break
|
||||
case 'checking':
|
||||
checkingForUpdateItem.visible = true
|
||||
break
|
||||
case 'downloading':
|
||||
downloadingUpdateItem.visible = true
|
||||
break
|
||||
case 'update-available':
|
||||
installUpdateItem.visible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Default list of menu items.
|
||||
//
|
||||
// Returns an Array of menu item Objects.
|
||||
getDefaultTemplate () {
|
||||
return [{
|
||||
label: 'Atom',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Check for Update',
|
||||
metadata: {autoUpdate: true}
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
accelerator: 'Command+R',
|
||||
click: () => {
|
||||
const window = this.focusedWindow()
|
||||
if (window) window.reload()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Close Window',
|
||||
accelerator: 'Command+Shift+W',
|
||||
click: () => {
|
||||
const window = this.focusedWindow()
|
||||
if (window) window.close()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Toggle Dev Tools',
|
||||
accelerator: 'Command+Alt+I',
|
||||
click: () => {
|
||||
const window = this.focusedWindow()
|
||||
if (window) window.toggleDevTools()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
click: () => app.quit()
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
focusedWindow () {
|
||||
return global.atomApplication.getAllWindows().find(window => window.isFocused())
|
||||
}
|
||||
|
||||
// Combines a menu template with the appropriate keystroke.
|
||||
//
|
||||
// template - An Object conforming to atom-shell's menu api but lacking
|
||||
// accelerator and click properties.
|
||||
// keystrokesByCommand - An Object where the keys are commands and the values
|
||||
// are Arrays containing the keystroke.
|
||||
//
|
||||
// Returns a complete menu configuration object for atom-shell's menu API.
|
||||
translateTemplate (template, keystrokesByCommand) {
|
||||
template.forEach(item => {
|
||||
if (item.metadata == null) item.metadata = {}
|
||||
if (item.command) {
|
||||
item.accelerator = this.acceleratorForCommand(item.command, keystrokesByCommand)
|
||||
item.click = () => global.atomApplication.sendCommand(item.command, item.commandDetail)
|
||||
if (!/^application:/.test(item.command, item.commandDetail)) {
|
||||
item.metadata.windowSpecific = true
|
||||
}
|
||||
}
|
||||
if (item.submenu) this.translateTemplate(item.submenu, keystrokesByCommand)
|
||||
})
|
||||
return template
|
||||
}
|
||||
|
||||
// Determine the accelerator for a given command.
|
||||
//
|
||||
// command - The name of the command.
|
||||
// keystrokesByCommand - An Object where the keys are commands and the values
|
||||
// are Arrays containing the keystroke.
|
||||
//
|
||||
// Returns a String containing the keystroke in a format that can be interpreted
|
||||
// by Electron to provide nice icons where available.
|
||||
acceleratorForCommand (command, keystrokesByCommand) {
|
||||
const firstKeystroke = keystrokesByCommand[command] && keystrokesByCommand[command][0]
|
||||
return MenuHelpers.acceleratorForKeystroke(firstKeystroke)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user