Developing desktop applications used to be a difficult and tedious task. Today, Electron makes desktop application development surprisingly easy. In this lesson, we are going to show you how quickly we can create a pomodoro timer app with Electron.
Project Structure:
main.js
- The main electron application scriptindex.html
- The web view for the main windowrenderer.js
- External Javascript for the main windowstyle.css
- External stylesheet for the main windowpackage.json
andpackage-lock.json
- Hold the details of your project and its dependencies
By now you should already have this repository cloned onto your computer. Navigate to lesson-2/starter-code
from the terminal and run npm install
to install all the necessary modules for this project.
If you open main.js
, you will see that we have already have some code to get you started. On line 2
, you should see
const { app, BrowserWindow, Menu } = require('electron')
which imports the necessary modules from electron. To create a window, we need to listen for electron’s ready
event, which gets fired when electron is ready to process your application script (line 31
):
app.on('ready', createWindow)
connects the ready event with a function called createWindow, and you will be writing the code to create your first window inside this function.
Let's start creating your first window, enter the following lines on at TODO: PART 2
let mainWindow = new BrowserWindow({width: 400, height: 300, resizable: false})
creates a 400x300
window that is not resizable. After we have our window, which is equivalent to a chrome tab, we can display a webpage called index.html
inside it with the following code:
mainWindow.loadFile('index.html')
If you run your app now with the command "npm start", you should have the following window
Electron can automatically create a top menu bar from json. Inside createWindow function at TODO: PART 3 - A
, we use
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
to tell electron to generate the application menu from the json object ‘template’, and set the application menu to the menu that we just generated.
You should also remove the following line, it was used to remove the default menu. Since we are creating our own menu, it is not needed anymore.
Menu.setApplicationMenu(null)
Now let’s define the menu object ‘template’ at TODO: PART 3 - B
:
let template = [{
label: 'Timer',
submenu: [{
label: 'Start'
}]
}]
If you run the app now, you will see that there is a button called Timer
on the top left. The Timer
button opens up a submenu with a single option Start
, but clicking the Start
button doesn’t do anything yet, because we haven’t connected the button with the renderer script.
Note: On macOS, your menu is on the top left. It is going to say Electron
instead of Timer
because in macOS the first menu item is always the name of the application. Since currently we are running the app through npm, it is always going to say Electron
. You will be able to change the name of your application when you package and deploy it.
This is probably the most important part of this lesson, and you will learn a very important topic of electron.
There are two js files inside your project folder so far, and they run in separate processes. main.js
runs in the application process, and renderer.js
runs in the window process. It is crucial to have each window to be sandboxed in its own process, so a crash of a single process does not make the entire application unresponsive. It is also good for security reasons.
However, communication between processes is difficult, but luckily, electron has an easy-to-use ipc(interprocess communication) API. Using this API, you can send any messages with a channel name between any processes.
Inside your menu template object (at TODO: PART 3 - B
), add a click handler for the start button:
click: function (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send('start timer', 5);
}
}
sends 5
in channel start timer
to the renderer process. Your menu template object should look like this:
let template = [{
label: 'Timer',
submenu: [{
label: 'Start',
click: function (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send('start timer', 5);
}
}
}]
}]
Finally, in renderer.js
at TODO: PART 4
, use
const ipc = require('electron').ipcRenderer
ipc.on('start timer', function (event, arg) {
startTimer(arg);
});
To receive 5
and pass it in function startTimer
to start the timer.
startTimer(timeInSeconds)
is a function already written for you in renderer.js
. If you call the function with a number n
, it will start an n
-second timer
Now you should have a basic pomodoro app working!
- Add more options to the menu to start different timers
- Add pause/resume button
- Add keyboard shortcut for starting and stopping