Creating a cross-platform Desktop application using Node.js

bonjour

Soooo… yeah, I’ve been going on a Node / JavaScript binge for the past month or so. There’s so much cool stuff you can do. Last time I wrote a blog post about cross platform command line apps and those are cool, this time I started playing around with a cross platform GUI application. There are a couple of competing technologies in the Node ecosystem if you want to build such a thing, they’re basically doing the same thing. NW.js is an Intel thing and Electron is a Microsoft thing. Electron is more popular supposedly so… I decided to go with that.

This is probably gonna be a 3 part series of blog posts just ‘cos it got so ridiculously long. Apologies for that 🙂

Electron

The idea behind Electron is simple. Node is already just basically the Javascript engine stripped out of the Chromium browser, plus a few libraries. Electron is just… adding some of the browser functionality back so that you can display a front end. ivermectin vs imidacloprid Bang in a few libraries to do common OS type things like display file open dialogs and Bob is well on his way to being your uncle. The reason this is cool is that you now get to build your front end with HTML. Good Lord! We can code like it’s 1999&nbsp. <br>
Forsooth!

Hello World

So… ja.. the actual point of this blog post. OK let’s go from the top. Electron apps are Node apps, so you can start with a good old

npm init && git init && npm install --save-dev electron

That gives you a package.json and installs electron as a dev dependency.

There’s a decent Hello World tutorial over at the Electron website so… ya know, head over there for more in-depth details, and I’ll just point out the stuff that I found interesting.

Baby Steps

We’re gonna add an index.html to be displayed when the user opens the app:

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

And in main.js we’re gonna load that page.

  const { app, BrowserWindow } = require("electron");

  const createWindow = () => {
    const win = new BrowserWindow({
      width: 800,
      height: 600
    });

    win.loadFile("index.html");
  };

  app.whenReady().then(() => {
    createWindow();
  });

If you run that you’ll see the index.html page displayed. To run it you’ll need to add a line like so: "dev": "electron ." to the scripts part of your package.json, and then npm run dev.

Adding CSS

OK so the page is very boring at present and what better way to unborify it than to add style? I just went and grabbed W3.CSS from here. But you can in principle use any CSS. treating goat lice with ivermectin

You add that to your page like normal in the <head> portion of index.html:

  <link rel="stylesheet" href="w3.css" />

And let’s add some styling information in the body:

  <div class="w3-panel w3-teal">
    <h1>Hello World!</h1>
  </div>

Much better 🙂 If you run that you will now see a nice styled banner displayed.

Customizing the menu

If you run the app as it is at present you’ll see you get the default Electron menu displayed, that’s a pain. We want to be able to add our own menu options to the app. This is all detailed in the Electron docs, by the way, just… not in one place. Let’s modify main.js. First we need a function to create the menu:

  const { Menu } = require("electron");

  const createMenu = () => {
    const isMac = process.platform === "darwin";
    const template = [
      {
        label: "&File",
        submenu: [
          isMac ? { role: "close" } : { role: "quit" },
        ],
      }
    ];
    const menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);
  }

Note the isMac thing up above. Of course Apple have to be different ‘cos Steve Freaking Jobs is a freaking genius or something. Apparently default behaviour for Apple programs is not to quit the application when the user tells them to… ya know… quit the application… but to just close all windows. Anyway this necessitates a few more modifications to main.js to make everything work:

app.whenReady().then(() => {
  createMenu();
  createWindow();

  app.on("activate", function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on("window-all-closed", function () {
  if (process.platform !== "darwin") app.quit();
});

Electron’s process model

You can find further details here but the essential thing to understand is that the Main process is the only process that’s allowed to communicate with the operating system in an Electron app, each Main process spawns multiple Renderer processes which are the things that actually get displayed as BrowserWindows.

Renderer processes do not have access to Node functionality, that’s all handled in the Main process. So when your user e.g. clicks a button in your app and you want to do operating system type things in response you have to communicate with the Main process via Interprocess Communication (IPC). Electron provides a few different ways to do this, we’ll explore it in a bit more detail later.

Adding functionality

So right at the moment all we have is an app that can display a webpage, which ya know… I might as well just use an internet browser. To really explore the goodness that is Electron we wanna actually make it do something so… the thing I thought was, let’s make this thing into a text editor.

We’re gonna need to:

  • Open and Save text files. This has to be done in the Main process.
  • Display them for editing. This obviously has to be done in the Renderer process, which means we need our Main and Renderer processes to communicate.

OK so the design is gonna be like this:

  • a textarea inside index.html to display text
  • File Open / Save / Save As functionality in our app menu
  • use the electron dialog module to display Open and Save dialogs using the operating system native ones
  • use Node fs module to actually open and save text files
  • Use the electron contextBridge, ipcMain and ipcRenderer modules to send and receive text between the Main and Renderer processes.

Continued in Part II

OK so we’re gonna leave it there for this blog post. If you want to go look at the final product which… I have to warn you is not anything really special… then the github link is here. Otherwise, I challenge you to do something fun with your own hello world Electron app involving opening and saving files, and displaying stuff on screen 🙂

About the Author