mirror of
https://github.com/atom/atom.git
synced 2026-02-12 15:45:23 -05:00
226 lines
7.5 KiB
JavaScript
226 lines
7.5 KiB
JavaScript
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.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)
|
|
}
|
|
}
|