Over at the forums there was a developer who needed to open a second app window from the main process when the user had done something in the renderer process. So I thought we could look at a real world example. So in this tutorial we’ll wait for the user to click a button. Then we’ll send a message to the main process which in turn opens a new window.
1. Setting up a new window in main.js
Fire up the Electron tutorial app or your own app, and open main.js. The additions are highlighted in bold.
We create a new variable to hold our second window with let secondWindow and by now you know how to create a new BrowserWindow. But this time the size of the window has changed. And we also don’t show a frame, so we set that to frame:false. This is because we close the window from a button that we create ourselves instead of using the standard os-buttons. If the user closes the new window with the built in close button they wont be able to open it again because it’s not initialized anymore.
We also set mainWindow as parent so that secondWindow will stay on top of the parent window. There are some platform specific handling to the parent setting:
On macOS the child windows will keep the relative position to parent window when parent window moves, while on Windows and Linux child windows will not move.
On Windows it is not supported to change parent window dynamically.
On Linux the type of modal windows will be changed to dialog.
On Linux many desktop environments do not support hiding a modal window
The secondWindow also loads another .html file called ipcwindow.html that is located in a folder called windows. Lets look at that next.
2. IPCWindow content
Create a new folder called windows. In that folder you create ipcwindow.html. Add this content to the html file:
3. Changes in navigation menu and who we are section
When this tutorial is being written the navigation menu and who we are section looks like this:
Navigation menu - who we are
Change the name of the menu-item to IPC/Remote in the en.js and sv.js translations
Also rename sections/whoweare.html to ipcremote.html. Then update link=rel and the sidebar menu in index.html:
In ipcremote.html we also need to update the id of the section to ipcremote and adding some information about what will happen if we click the button:
Now we’ve got our app set up and it’s finally time to move on to sending and receiving messages with IPC. This will be done asynchronous.
Create a file called ipc.js in assets/js/. To start with you can add this content to it:
init() is called on documentready. That in turn hooks up an onclick event on the button in the ipc section with id #open-secondwindow-button. Now to the ipc stuff. When that button is clicked ipcRenderer.send is invoked like this: ipcRenderer.send(‘open-second-window’, ‘an-argument’) The first value sent as a parameter is the name of the event. The second one is an argument of your own choice.
and add a require statement to the file in index.html:
Now it is time to listen for this event in main.js. Start by adding a require statement for ipcMain at the top of the file:
And to listen to the event and showing the second window just add this code:
And at this point the tutorial on ipc main and renderer is over. But we’ll do it once more just to be able to close the secondwindow again.
6. Closing a BrowserWindow
Open up ipc.js again and add another on click event that sends a closing message to main:
And in main we add another event listener:
Ipc also needs to be required from ipcwindow.html:
That’s it for this tutorial. Check out the official documentation on IPCMain and IPCRenderer
You might have come across the main process and renderer process in Electron. This is a simple explanation of them.
The main process takes care of starting and running your app. In the electron tutorial app you can see this line in package.json:
This is the primary entry point to an electron app. And this is running in the main process. All the files you require from this file will be running in the main process as well.
If we would call process.type and log it with console.log() the word “browser” would turn up in the terminal running the app.
console log in main process
The renderer process takes care of showing your app in the Chromium browser. When the app is ready to show the electron tutorial app runs this command in main.js:
console log in renderer process
So what API calls can we do from the different processes? The official documents are kind to us and list them under Main Process and Renderer process. But there are also api-calls that work in both processes.
Communicating between the processes
The api allows us to communicate between the processes. You can do that with remote which provides a simple way to do inter-process communication between renderer and the main process.
IPCRenderer and IPCMain
The api also allows us to use ipcRenderer and ipcMain to help us send events between the different processes. We can then listen to these events and implement the functionality we need.
Let us add localization to the electron tutorial app. We will look at translating both in the main and renderer process.
I tried some of the already existing npm-packages that helps with translating your app. They were good but didn’t feel 100% right to me. Some of them used .po-files, some of them were too advanced. So I decided to write a simpler one on my own. This is just a matter of taste. If you find another way that suits you better you should do it like that instead.
The localization script will store translations in .js files with a json-structure. It will use english as a fallback language, and try to use the users language by default. If no translation is found the original phrase will be sent back to be used.
1.Adding the script
Create a new folder in the project called translations. Create a new .js file and copy the contents below and save the file as i18n.js (why i18n?)
In the i18n constructor the script first checks if a language file for the current users locale exists. If it does that file is loaded. If the file does not exist the script loads the fallback language in en.js.
the __ function takes a phrase as an argument and checks if that is translated in the loadedLanguage. If it’s not it returns the phrase sent to the function.
This line takes care of loading app from either the main(mainmenu.js) or renderer(from index.html) process.
2.Adding translation files
Since i am from Sweden I will locate the tutorial app into Swedish. So I will create en.js and sv.js in the translations folder. To know what you should name your file so that the i18n script will find it and load it you can add this to main.js which will log the locale to the terminal(just add .js to it):
This is how the structure in the translation files should look. This is what en.js will look like:
And this is what my sv.js looks like:
3.Translating the menu (Main process)
As you might have guessed when reading the translation they look like menu items. So let us translate the menu that was added in the last tutorial.
Open up mainmenu.js that is located in the menu folder and add this require statement to the top of the file:
Now all we need to do is call i18n.__() for every menu item. These are what the first three looks like, here is the fully translated menu file.
What you do is add the label: attribute to the menu item, which gets the translated value from i18n__().
4.Translating navigation menu in index.js (Renderer process)
Create a new file in assets/js/ called translations.js. In this one we’ll use jquery to set the translated strings to our html-markup.
Open index.html and add i18n int the list of scripts.
In translations.js it’s plain jquery to set the text of the elements that you wish to translate:
This is just two translations that are added. You can see the full file here.
The app now looks like this when running it on an os set to Swedish language:
electron tutorial app windows - translated to swedish
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):
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.
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.
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.
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:
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.
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)
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.
When a menuItem is clicked you can define what code will be executed by defining a click event.
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.
In this electron packager tutorial we will look at how to create MacOS, Windows and Linux executables with an app icon. This is also a continuation of the Electron app icon post, so start there if you haven’t read it (It’s short, i promise).
Electron packager is created by electron-userland and this is what they say about it:
"Electron Packager is a command line tool and Node.js library that bundles Electron-based application source code with a renamed Electron executable and supporting files into folders ready for distribution."
So lets go ahead and install it. Run these commands in the terminal in the app folder:
2. Setting productname and electron version
Electron packager looks for a product name in package.json, so lets go ahead and add one. We also need to add what version of electron to package the app with.
Lets begin with the electron version. We’ll add that from the terminal with this command:
Now when that is done open up package.json and add a productname:
3. Building MacOS, Windows and Linux package from the terminal
To get to know what all these flags do, and what more flags exists you can read the electron-packager api.
Now you can run this command from the terminal to build a package for mac:
Electron packager mac
And to build for Windows you can run this from the git bash:
Electron packager windows
Electron packager ubuntu linux
overwrite: replaces any existing output directory when packaging.
platform: The target platform to build for
arch: the architecture to build for
icon: sets the icon for the app
prune: runs npm prune –production before packaging the app. It removes unnecesary packages.
out: the name of the directory where the packages are created.
To make it easier to create new builds we can create scripts in package.json so that we don’t have to remember all these settings. Add the scripts below, making your package.json look like this: