My name is Steve. I have a love ~ hate relantionship with Javascript. And, I just spent way too much time playing with Electron.js this past month.
Raise your hand if you know what it is? Raise your hand if you've built an application with it before? It's a desktop application framework built on top of Chromium and Node.js. It abstracts standard native GUI with a simple API. Node.js is used for all other low level interactions (e.g. file system manipulation)
What apps are built with it? Visual Studio Code, Slack, Atom.
Git Book: Uses git to collaborate writing documentation, books and research papers; Avocode: Helps prepare mocks for a web application; Zoommy: Helps you find good free stock photos; Mapbox: Tool to help style and design maps.
Because you have web development skills, you can do mobile apps already, but you also want to desktop apps. This is an easy framework to work with that gives you a decent native API.
The talk assumes that you know some basic Javascript. You will see some ES6 JS, but only syntax supported in Node 4.1. I'm not using any build tools for this project nor am I using any frontend framework. I'm keeping it simple.
Building an obligatory "Hello, world!" app. Before that, I want to give overview of how Electron works.
Assuming you have Node.js installed. This is how you get started.
Emphasize lines are important. You could install Electron.js globally and just run `electron .`, but that would be less portable. `npm start` runs the electron binary downloaded in the node_modules folder.
I have app folder. Inside, I keep assets, the main process and renderer processes in their own folder.
My entry file for the main process just requires a main js. We wait for the application to be loaded and ready, then we create the main window with the BrowserWindow module. The module is part of Electron's Native GUI API that only the main process is allowed to use.
The BrowserWindow is created with an html file. You can load stylesheets, scripts or anything else that might load in a web page.
That's it for "Hello, world!". We just run the start script.
Building an obligatory "Hello, world!" app. Before that, I want to give overview of how Electron works.
Electron boots > Runs entry file in package.json as Main process (runs like any other Node.js script) > BrowserWindow module create new renderer processes (behaves just like a web page).
Communicating between processes is fairly simple. The inter-process communication (IPC) can create channels that both, main and renderer, processes can listen on. Their interface is nearly the same as events.
To set it up, we create an listener on a channel and give it a handler.
In the other process, we simply send messages to the channel.
In this scenario, the handler for the "asynchronous-message" channel sends off a reply to the "asynchronous-reply" channel. The renderer process is ready for it and prints out a reply.
A very common use case for communicating to Main is to call Native GUI modules that only the main process can do. The Remote module abstracts that communication. Instead of IPC, I'll be using the Remote module throughout this talk.
To demo Electron's Native GUI interface, I'll build an app named Animal Advisor.
It's a silly app that generates advice animals like these. I won't get into the details of how I built the generator. Suffice it to say that it uses the Meme Captain API & I spent too much time on it!
What's changed? I've added CSS & fonts. There is javascript file executed by main/index.html. And, we've added an internal library. That's the bit that fetches the generated Advice Animals. It makes use of the Meme Captain API.
Here it is in action. Type a sentence. It's compared to some matchers, then the relevant advice animal is generated.
We're going start by implementing a system notification that triggers once the image is ready.
Electron doesn't implement it's own notification module. It makes use of the HTML5 Notification API.
And, this is what it looks like!
We're going start by implementing a system notification that triggers once the image is ready.
Before creating the tray icon, we'll create a menu for it. Otherwise, we'll just have a tray that does nothing. To create a menu, we get the Electron module, menu. Create a template. The first item in the menu will toggle the chromium dev tools. We pass the menu item a click handler and which toggles it on the mainWindow.
The second menu item implements a quit command. It's a standard action that is implemented by Electron. selector: "terminate:" will do.
Finally, we create the Menu object with .buildFromTemplate. This gives us the menu that we'll bind to the Tray icon.
Before creating the tray icon, we'll create a menu for it. Otherwise, we'll just have a tray that does nothing. To create a menu, we provide the `buildFromTemplate` method array of objects. The accelerator property is Electron's API for creating application keyboard shorcuts. We'll see more of it later. **What is the selector?**
We create the Tray icon, but create a new instances of it. It takes a path to an icon which can be a png. We can then set a tool tip and a context menu which is the menu we created in the prior slides.
Here's a demo.
The menu at the top of your application. It commonly has File, Edit, Window, Help and more as menu items.
All menus make use of the same module. The tray icon menu, application menu and context menu. For OS X (only), the first menu item (or object) in the array will always be named after the application. This is the menu item we see next to the Apple icon.
Each application menu items have their own submenu. Or, the menu that appears when you click on File or Window.
The role property on the submenu are for standard actions provided by the OS. In this case, Electron gives us the ability to implement about, services and hide simply by defining a role on the sub menu item.
Still in the config array, we create another menu labeled "Window". It's given the role of window, a standard menu. This is the window menu you see in just about every OS X applicaton. Like a standard action, we can just give it the role of window.
Window has standard submenu items. You're probably all familiar with minimize, close and front. Electron has several other standard menus and standard sub menus that you can check out in its docs.
Finally, we pass our configuration array to buildFromTemplates. Using the same Menu module, we set the newly created menu as our app's application menu.
Voila!
We haven't done a whole lot of interesting things with our menus so far. What if I want a context menu that I can use to save our Advice Animal to disk or send to the clipboard? Let's start with the Context Menu first.
We'll be working in renderer process for our main window. Because menu creation is a native operation, only main process can create them. We could use the IPC module to send events over a channel and handle them in main process. Instead, we'll just use Electron's Remote module which abstracts that for us.
This time around. We're working with Menu and MenuItem methods to create it. We'll handle menu clicks with callback functions. This gives us a lot of freedom.
Finally, we add an event listener for the contextmenu event on the advice animal image element. We'll prevent the regular menu from showing, then we'll popup the menu we just created on the click location (by default).
This is what it looks like.
It's a common feature to be able to save an image to file by accessing its context menu. Let's add that ours for the advice animal image. We'll have the user pick the save location with a native save dialog.
To begin, let's require the dialog module. We'll have to use it through the Remote module, because it's a native command. It can only be called from within the main process. We're also requiring the shell module which we'll need to complete the effect.
Then, let's add a "Save Image as..." item to the context menu and give it a handler to create the save dialog.
We pass the showSaveDialog the current window, options and a callback. All arguments are optional. The callback will... (next slide)
...will be called with a path if the user saved and didn't cancel. In the handler, we just copy the cached picture to the location the user chooses with the save dialog. The handleCopy handler is called when the file copy is finished.
In case the copy fails, we'll make use of the error dialog to alert the user. It only takes a title and text content as arguments.
If there is a path (that is to say the user didn't cancel and there were no errors), we'll use the Shell module to open a finder (file explorer) window to save destination. Shell can be used in any process and it can be used to open folders, items and urls with system default applications. You can also use it send system beeps and send items to the trash.
If there is a path (that is to say the user didn't cancel and there were no errors), we'll use the Shell module to open a finder (file explorer) window to save destination. Shell can be used in any process and it can be used to open folders, items and urls with system default applications. You can also use it send system beeps and send items to the trash.
As a consumate shorcuter, I can't stand applications without good clipboard integration. Let's use Electron's API to integrate it.
Writing to the clipboard is trivial. It's writable from both processes, main and renderer. First, we require the module.
We create a new menu item for the copy command.
Finally, I write the image to the clipboard when it is set and when the *copy* menu item is clicked. Passing image path to writeImage will do the job.
There several other methods to write and read html, text and images from the clipboard.
**This should take more than one slide** We’re going to go back an make global shortcuts, menu shortcuts and notifications configurable. We’re probably going to use `nsconf` for this. *See sound box tutorial*
I didn't talk about all the interesting things supported by Electron. Here's a few I missed.
An interface for the Squirrel auto-updater framework.
A module to register/unregister global keyboard shortcuts with the operating system. I really wanted to show how this is done, but I ran out of time. However, you can bet that the release of Animal Advisor will support it.
One is used to add event listeners to power states. The other is used to prevent the desktop from entering power saving modes.
This modules enables sending your apps crash reports.
It's time to package a release! Thankfully, there's a npm package that makes trivially easy for us.
Install electron packager as a development dependency.
We'll configure a packaging script for ease of use. The first purple text is your application name; the yellow, desktop platform; the pink, destination folder; the green, electron version; and, the last purple is the location of the app icon.
A small application like this builds pretty fast. You don't need to see this in action, but I felt I was on a roll with these video demos.
I encourage you to use all the build tools you're familiar with. Electron works especially well with your favorite frontend framework. I will likely rewrite this application in React and build it with Webpack. Also, I just wanted to remind you that you have access