Electron menu

Electron menu

This tutorial shows you how to add an Electron menu to your app. It will also make standard keyboard shortcuts, like copy and paste, work on MacOS.

We’ve actually got our work pretty much done for us thanks to the Electron documentation giving us a good template to use. We just need to implement and do some changes to it (If you want to that is). As always this tutorial uses Electron tutorial app as a canvas so you can download and/or look at the code on github.

1.Adding the menu

Open up a new file and paste this menu definition to it (this is the code from the documentation, with minor adjustments):

const {Menu} = require('electron')
const electron = require('electron')
const app = electron.app

const template = [
  {
    label: 'Edit',
    submenu: [
      {
        role: 'undo'
      },
      {
        role: 'redo'
      },
      {
        type: 'separator'
      },
      {
        role: 'cut'
      },
      {
        role: 'copy'
      },
      {
        role: 'paste'
      },
      {
        role: 'pasteandmatchstyle'
      },
      {
        role: 'delete'
      },
      {
        role: 'selectall'
      }
    ]
  },
  {
    label: 'View',
    submenu: [
      {
        label: 'Reload',
        accelerator: 'CmdOrCtrl+R',
        click (item, focusedWindow) {
          if (focusedWindow) focusedWindow.reload()
        }
      },
      {
        label: 'Toggle Developer Tools',
        accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
        click (item, focusedWindow) {
          if (focusedWindow) focusedWindow.webContents.toggleDevTools()
        }
      },
      {
        type: 'separator'
      },
      {
        role: 'resetzoom'
      },
      {
        role: 'zoomin'
      },
      {
        role: 'zoomout'
      },
      {
        type: 'separator'
      },
      {
        role: 'togglefullscreen'
      }
    ]
  },
  {
    role: 'window',
    submenu: [
      {
        role: 'minimize'
      },
      {
        role: 'close'
      }
    ]
  },
  {
    role: 'help',
    submenu: [
      {
        label: 'Learn More',
        click () { require('electron').shell.openExternal('http://electron.atom.io') }
      }
    ]
  }
]

if (process.platform === 'darwin') {
  const name = app.getName()
  template.unshift({
    label: name,
    submenu: [
      {
        role: 'about'
      },
      {
        type: 'separator'
      },
      {
        role: 'services',
        submenu: []
      },
      {
        type: 'separator'
      },
      {
        role: 'hide'
      },
      {
        role: 'hideothers'
      },
      {
        role: 'unhide'
      },
      {
        type: 'separator'
      },
      {
        role: 'quit'
      }
    ]
  })
  // Edit menu.
  template[1].submenu.push(
    {
      type: 'separator'
    },
    {
      label: 'Speech',
      submenu: [
        {
          role: 'startspeaking'
        },
        {
          role: 'stopspeaking'
        }
      ]
    }
  )
  // Window menu.
  template[3].submenu = [
    {
      label: 'Close',
      accelerator: 'CmdOrCtrl+W',
      role: 'close'
    },
    {
      label: 'Minimize',
      accelerator: 'CmdOrCtrl+M',
      role: 'minimize'
    },
    {
      label: 'Zoom',
      role: 'zoom'
    },
    {
      type: 'separator'
    },
    {
      label: 'Bring All to Front',
      role: 'front'
    }
  ]
}

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

Save this in a new folder called menu, and why not name the file mainmenu.js

If you take a look at the two last lines in mainmenu.js you see two calls, one to buildFromTemplate(), which basically takes the array defined above, and creates a menu from it. Then we pass the menu to setApplicationMenu()

2. Showing the menu in the app

Now open up main.js and add require(‘./menu/mainmenu’) after creating the window. In the Electron tutorial app it is added to line 45, right before closing the createWindow function. We add it there because the call to setApplicationMenu needs to been done after the ready event is fired. Which it has in createWindow.

function createWindow () {
    ...

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
       // Dereference the window object, usually you would store windows
       // in an array if your app supports multi windows, this is the time
       // when you should delete the corresponding element.
       mainWindow = null
    })

    require('./menu/mainmenu')
}

If you start the app now, you can see that you have a different menu already.

3. Menu items

Let us look at what the mainmenu.js contains. It’s an array that defines MenuItems.

Label

Labels are what the menu item will say when it is displayed. It is required to set when you are defining a menu item that does not use Role(which we will look at soon). You can use this when localizing your menu items.

A submenu is what you think it is. It is an array containing menuitems for a submenu.

Role

Electron provides us with roles that are predefined for Platform specific menu items such as undo, redo and those standard functions. The existing roles are:

  • undo
  • redo
  • cut
  • copy
  • paste
  • pasteandmatchstyle
  • selectall
  • delete
  • minimize - Minimize current window
  • close - Close current window
  • quit - Quit the application
  • togglefullscreen - Toggle full screen mode on the current window
  • resetzoom - Reset the focused page’s zoom level to the original size
  • zoomin - Zoom in the focused page by 10%
  • zoomout - Zoom out the focused page by 10%

On macOS role can also have following additional values:

  • about - Map to the orderFrontStandardAboutPanel action
  • hide - Map to the hide action
  • hideothers - Map to the hideOtherApplications action
  • unhide - Map to the unhideAllApplications action
  • startspeaking - Map to the startSpeaking action
  • stopspeaking - Map to the stopSpeaking action
  • front - Map to the arrangeInFront action
  • zoom - Map to the performZoom action
  • window - The submenu is a “Window” menu
  • help - The submenu is a “Help” menu
  • services - The submenu is a “Services” menu

When specifying role on macOS, label and accelerator are the only options that will affect the MenuItem. All other options will be ignored.

Accelerator

If you scroll down a bit in mainmenu.js to line 43 you can see something called accelerator. With the accelerator you can add keyboard shortcuts to your menu item. Built in accelerators are:

  • Command (or Cmd for short)
  • Control (or Ctrl for short)
  • CommandOrControl (or CmdOrCtrl for short)
  • Alt
  • Option
  • AltGr
  • Shift
  • Super

One interesting accelerator to look at is the CommandOrControl. It uses the command button on macs, but on windows it maps to control instead since Windows doesn’t use the command button.

click

When a menuItem is clicked you can define what code will be executed by defining a click event.

Type

You can se a type to a menuitem, a type can for example be a separator. If you look at line 56 in mainmenu.js you can see it being used. But there are more types defined:

  • type String - Can be normal, separator, submenu, checkbox or radio.

Read more

To learn more about this topic you can read about Menu and MenuItem in the official docs. You can also take a look at the implemented menu on the Electron tutorial app repository on github

Next step in this tutorial series walks you through how to translate the menu and the rest of your electron app