• 4 must-know tips for building cross platform Electron apps
  • This article is authorized by the original author, Kilian Valkhof
  • The Nuggets translation Project
  • Translator: huanglizhuo
  • Proofread by: DeadLion, Zhouzihanntu

Electron, a technology used by many apps including Avocode, lets you quickly get a cross-platform desktop application up and running. If you don’t pay attention to some problems, your app will soon fall into a pit. Doesn’t stand out from other apps.

This is a handwritten version of my talk at the Electron Meetup in Amsterdam in May 2016, with consideration for API changes. Note that the following will go into great detail, assuming you know something about Electron.

First of all, who AM I

I’m Kilian Valkhof, a front-end engineer, UX designer, and app developer, depending on who you’re asking. I have over 10 years of Internet experience, building desktop applications in a variety of environments, such as GTK and QT, as well as Electron.

You might want to try Fromscratch, a free cross-platform app THAT I recently developed for auto-saving notes.

In developing Fromscratch, I spent a lot of time making sure the application ran well on all three platforms and figured out how to do it in Electron. These are all accumulated in the process of digging and filling pits.

It’s not hard to use Electron to make your app feel good and consistent, just pay attention to the following details.

1. Copy and paste on macOS

Imagine you launch an app for taking notes. You’ve been using and testing it many times on a Linux machine, and you’ve received a friendly message on ProductHunt:

 if (process.platform === 'darwin') {

       var template = [{

         label: 'FromScratch',

         submenu: [{

           label: 'Quit',

           accelerator: 'CmdOrCtrl+Q',

           click: function() { app.quit(); }

         }]

       }, {

         label: 'Edit',

         submenu: [{

           label: 'Undo',

           accelerator: 'CmdOrCtrl+Z',

           selector: 'undo:'

         }, {

           label: 'Redo',

           accelerator: 'Shift+CmdOrCtrl+Z',

           selector: 'redo:'

         }, {

           type: 'separator'

         }, {

           label: 'Cut',

           accelerator: 'CmdOrCtrl+X',

           selector: 'cut:'

         }, {

           label: 'Copy',

           accelerator: 'CmdOrCtrl+C',

           selector: 'copy:'

         }, {

           label: 'Paste',

           accelerator: 'CmdOrCtrl+V',

           selector: 'paste:'

         }, {

           label: 'Select All',

           accelerator: 'CmdOrCtrl+A',

           selector: 'selectAll:'

         }]

       }];

       var osxMenu = menu.buildFromTemplate(template);

       menu.setApplicationMenu(osxMenu);

   }
Copy the code

If you already have a menu, you will need to add the above cut/copy/paste commands to your existing menu.

1.1 add icon

. Otherwise your application on Ubuntu looks like this:

Many apps have this problem because on Windows and macOS, the icon that appears in the taskbar or dock is the app icon (an.ico or.icns), whereas on Ubuntu it is your window icon. . Adding this is easy. In the BrowserWindow option, declare icon:

mainWindow = new BrowserWindow({

     title: 'ElectronApp',

     *icon: __dirname + '/app/assets/img/icon.png',*

   };
Copy the code

This will also show a small icon in the upper left corner of your Windows app.

1.2 UI Text Is not optional

When using a browser, text editor, or other native apps like Chrome, you should be aware that you can’t select text from a menu. One way to make your app weird in Electron is to accidentally trigger text selection, or highlight UI components.

CSS is here to help: Add the following code to all buttons, menus, or any other UI element:

 .my-ui-text {

       *-webkit-user-select:none;*

   }
Copy the code

So the text is not selectable. It’s more like a native app. One of the easiest ways to test this is to CTRL/CMD + A to select all the text options in your app. This will help you quickly identify which ones need to be added.

1.3 You need to use three different ICONS on three different platforms

Honestly, it’s a real inconvenience, on Windows you need.ico files, on macOS you need.icns files, and on Linux you need.png files.

Fortunately, a normal PNG image can generate two other ICONS. Here is the most convenient way to do it:

1. Make a PNG of 1024×1024 pixels, which means you’re nearly 1/3 of the way through. (Linux, check!)

2. For Windows, use icoTools to generate.ico:

icontool -c icon.png > icon.ico

3. For macOS, generate ICNS with png2ICns:

png2icns icon.icns icon.png

4. It’s done!

There are ALSO GUI tools like img2ICns on macOS, or Web tools like Iconverticons, but I haven’t used them.

1.4 Unexpected joy!

The electron packager does not require additional ICONS to select the correct icon for a given platform:

$electron packager. MyApp *--icon=img/icon* --platform=all --arch=all --version=0.36.0 --out=.. /dist/ --asarCopy the code

Well, I only found this out after I wrote the build and used different icon scripts for different versions 🙁

2. The white loading state is a browser behavior

Nothing better represents the essence of Electron App being just an embedded browser than a white loading. However, there are two ways to avoid the loading state:

2.1 Specify the BrowserWindow background color

If your app doesn’t have a white background, be sure to specify it explicitly in the BrowserWindow option. This doesn’t stop the solid color block when the app loads, but at least it doesn’t change color halfway through:

 mainWindow = new BrowserWindow({

     title: 'ElectronApp',

     *backgroundColor: '#002b36',*

   };
Copy the code

2.2 Hide your app before it loads:

Because the application is actually running in the browser, we can choose to hide the window until all resources are loaded. Before you start, make sure to hide the browser window:

 var mainWindow = new BrowserWindow({

       title: 'ElectronApp',

       *show: false,*

   };
Copy the code

Then, when everything is loaded, the window displays and focuses on it to alert the user. The “ready-to-show” event implementation of BrowserWindow or the “did-finish-load” event of webContents is recommended.

 mainWindow.on('ready-to-show', function() {

       mainWindow.show();

       mainWindow.focus();

   });
Copy the code

Remember to call foucs here to remind the user that your application has finished loading.

3. Keep the size and position of the window

This problem also exists in many native apps, and I find it to be one of the most frustrating. An app that once handled one location very well now has all the default locations again when it restarts, which makes sense for developers, but makes you want to hit the wall. Don’t do that.

Instead, save the size and location of the window and restore it every time you reboot, your users will appreciate it.

3.1 Precompilation scheme

There are two kinds of precompilation schemes: electron-window-state and electron-window-state-manager. You can do both, read the documentation well and watch out for boundary conditions, such as maximizing your application. If you really want to compile quickly and see the finished product, you can use either of these options.

3.2 Handle scrolling yourself

You can do the scrolling yourself, and that’s exactly what I used, based on some code I wrote for Trimage a few years ago. It doesn’t require a lot of code, and it gives you a lot of control. Here’s a demo:

3.2.1 Save the status

First we have to store the location and size of the application somewhere. This is easy to do with Electron Settings, but I chose to use Node-localstorage because it’s simpler.

var JSONStorage = require('node-localstorage').JSONStorage;

   var storageLocation = app.getPath('userData');

   global.nodeStorage = new JSONStorage(storageLocation);
Copy the code

If you save the data to getPath(‘userData’), Electron will save it to its own app Settings, in the ~/.config/YOURAPPNAME location, which on Windows is the AppData folder in your user folder.

3.2.2 Restore your state when opening the app

var windowState = {};

     try {

       windowState = global.nodeStorage.getItem('windowstate');

     } catch (err) {

       // the file is there, but corrupt. Handle appropriately.

     }
Copy the code

Of course, it doesn’t work the first time, so you have to deal with that. You can provide default Settings to set the size of BrowserWindow using the saved state information once you get the previous state in the JavaScript object:

var mainWindow = new BrowserWindow({

     title: 'ElectronApp',

     x: windowState.bounds && windowState.bounds.x || undefined,

     y: windowState.bounds && windowState.bounds.y || undefined,

     width: windowState.bounds && windowState.bounds.width || 550,

     height: windowState.bounds && windowState.bounds.height || 450,

   });
Copy the code

As you can see, I add the default Settings by providing fallback values.

Now in Electron, we can’t start the app in a maximized state when we start it, so we have to maximize the window after BrowserWindow is created.

// Restore maximised state if it is set.

   // not possible via options so we do it here

   if (windowState.isMaximized) {

     mainWindow.maximize();

   }
Copy the code

3.2.3 Save state when Move ReSize and Close:

In an ideal world you would only save the state of your window when closing an app, but in reality it misses a lot of application terminations for unknown reasons, such as power outages.

Capturing and saving the state at each Move Resize event allows us to restore the position and size of the last known state.

['resize', 'move', 'close'].forEach(function(e) { mainWindow.on(e, function() { storeWindowState(); }); }); And the storeWindowState function: var storeWindowState = function() { windowState.isMaximized = mainWindow.isMaximized(); if (! windowState.isMaximized) { // only update bounds if the window isn't currently maximized windowState.bounds = mainWindow.getBounds(); } global.nodeStorage.setItem('windowstate', windowState); };Copy the code

The storeWindowState function has a slight problem: If you minimize a maximized state native window, it will return to the previous state, which means that we want to save is to maximize originally, but we don’t want to overwrite the previous one the size of the window (not maximize window), so if you maximize, closed and reopened, cancel the maximization, And the position that you apply is the position that you maximized.

4. Some tips

Here are some tips that are small, short and useful.

4.1 the shortcut

In general, Windows and Linux use Ctrl, while macOS uses Cmd. To avoid adding each shortcut key (called Accelerator in Electron) twice, you can use “CmdOrCtrl” to set all the platforms at once.

4.2 Using system Fonts San Francisco

Using the default font means that your application will look compatible with the operating system. To avoid setting fonts individually for each system, you can quickly implement system-specific fonts with the following CSS blocks:

 body {

     font: caption;

   }
Copy the code

“Caption” is the keyword in the CSS that connects to the system-specified font.

4.3 System Color

As with System fonts, you can also use System colors to let the System decide which colors you want to apply. This is actually a partially implemented property that was deprecated in CSS3, but will not be deprecated anytime soon.

4.4 the layout

CSS is a pretty powerful layout, especially when combined with Calc () and Flexbox, but that doesn’t lessen the amount of work that needs to be done in older GUI frameworks like GTK, Qt, or Apple Autolayout. You can implement your app’s GUI in a similar way with Grid Stylesheets, a constrained layout system.

Thank you!

Building applications in Electron is fun and rewarding: you can get a cross-platform application up and running in a fraction of the time. If you’ve never used Electron before I hope this article piqued your interest enough to try it out. Many of the Harvest Electron sites have full documentation and lots of cool demos for you to try out its API

If you’re already writing Electron, I hope this encourages you to think more about how well your app is performing on all platforms.

Finally, for any other tips, please leave it in the comments section.