📜 ⬆️ ⬇️

Electron: from motivation to publication

- I would like to have an application that can do something that I need. Sorry for this.
- Why don't you write it yourself?
- It is difficult, it will take me a lot of time to understand the theory, begin to practice and, most likely, nothing good will come of it. In general, I am a frontend-developer and got used to HTML, CSS and JS. This stack does not allow writing desktop applications.
- Electron!


I'm sure most of us share screenshots. There are a large number of applications that can take screenshots and somehow edit them (crop, draw something, etc.). The first 3 that come to my mind: Joxy, Monosnap, Gyazo.


It would seem, what else to desire when there is a task and tools for it? But no, it's not that simple.


The criteria by which I was looking for the application


Saving the screenshot on the server


It is much more convenient to throw off the link to the screenshot, than to look for where the application saved the picture, and then attach it. Of course, some people will not like the idea of ​​storing screenshots in an unknown location. But, on the other hand, it allows you to quickly solve the problem - to share a screenshot. This model of application behavior suits me perfectly.


Drawing on the screenshot


From my own experience I can say that the arrow is present in almost every screenshot. It is much easier to point out some detail than to write something like: “This is the thing between ...”. Also, the standard set of drawing tools on the screenshot, in my opinion, should include a pencil, a rectangle and text. At the same time there should be an opportunity to move, turn over and delete the drawn element.


Linux version


As it turned out, not many applications exist for this task under Linux. For a long time, I used Gyazo and would use it further if I didn’t have to pay for the “rice-paddies” in the screenshot.


From the box


The application should immediately work as I need. I do not want to customize anything, register for any Imgur, etc. I just want to take a screenshot, sprinkle, save and share a link to it.


Throughout the year, I occasionally did a search, but in the end I did not find an application that meets my requirements.


Electron


Time passed and I decided to get acquainted with Electron, to understand that he knows how to in principle. For those who do not know, Electron is a technology that allows you to create desktop applications using HTML, CSS and JavaScript.


And one of the demos was shown getting a screenshot of the user's screen. This meant the following:



Where to go, where to start, how it works


I ask myself these questions every time I want to learn something new. Often the solution is the same - the documentation and some official “Get started”. Electron has everything in order with both. At least they write on the project's website - “It's easier than you think”. It really is! The only thing you need to immediately understand is that the application consists of 2 processes: Main and Renderer. In Main, we work with application windows, menus, etc., and in Renderer we have, in fact, just our page.


How to start the application


First let's create a project with the following structure:


- package, json
- main.js
- scripts
- renderer.js


To begin with, this is enough for us. In main.js, we will have the code related to the main process, and in scripts, the renderer.js render. Now you need to create the application itself. To do this, write to main.js:


const electron = require('electron'); const app = electron.app; app.on('ready', () => { createWindow(); }); app.on('window-all-closed', function() { app.quit(); }); 

What did we do in the end? We connected Electron, created the application itself and described two handlers. When our application is ready, we will call the function to create a new window. And at that moment when all the windows are closed, we kill the application process.


Window


Ok, now we have the application process itself. Now I would like to create a window for it. Remember we called the function createWindow () ;? So it's time to describe it.


 function createWindow() { appWindow = new BrowserWindow({ width: 300, height: 300 }); }; 

You also need to connect:


 const BrowserWindow = electron.BrowserWindow; 

Thus, we created a window with the specified dimensions. Very simple, isn't it?


image


A window has so many properties, methods, and events associated with it. For example: title, icon, x, y, show (), hide (), close (), minimize, closed, restore, resize. List the entire list is useless, so here is a link to Browser WIndow.


But looking at the empty window is not very fun, right? Let's upload there some simple page that contains the title “Hello, world!”. On the level with main.js create an index.html file that looks like the most common page with <h1>Hello, World!</h1> . Now it only remains to expand the createWindow () function by adding


 appWindow.loadURL(`file://${__dirname}/index.html`); 

Run our application and see:


image


Straight some magic :) But for a moment about sad things.


Debag


But what about actually catching errors? Everything is as simple as possible! If there is an error in the main process, then it will fall out to you in the console, from which you start the application. To catch errors on the page itself, we will use the browser console. Yes exactly! Our application in fact is a page running in the browser. To enable the console, let's add to createWindow(); line: appWindow.webContents.openDevTools(); . Now when you start the application, we will immediately see the console.


Menu


So far, all we got is a page in the window. I want to somehow interact with it, for example, using the menu. To do this, Electron has a special class Menu. We set ourselves a task - to create a menu from one element, when clicked, we will see a system pop-up window with the title “Hello”, a message: “Do you like this?” And answer options ['Yes', 'No'].


In main.js we write:


 const appMenu = require('./scripts/appMenu'); 

And create the appMenu.js file in the scripts directory. Inside it contains the following code:


 const { shell } = require('electron'); const dialog = require('electron').dialog; const newShotDialog = { type: 'info', title: 'Hello', message: 'Do you like this?', buttons: ['Yes', 'No'] }; module.exports = function appMenu(app, appWindow) { return ( [ { label: 'File', submenu: [ { label: 'Click me', click() { dialog.showMessageBox(newShotDialog, function(index) { }) }, }, ], }, ] ); }; 

We go further. In app.on('ready'... add:


 const template = appMenu(app, appWindow); const menu = electron.Menu.buildFromTemplate(template); electron.Menu.setApplicationMenu(menu); 

After starting the application, we will see that our menu has become smaller and now contains one item. By clicking on it, we will see the following:


image


Actually, we solved the task. I could devote the whole article to creating some abstract application (for example, an audio player), but then it would all consist of “some kind of code” -> “result”, which, you see, is not very interesting (personally, I would I closed the tab and went to write something of my own). Therefore, I will give the rest of the article a review of some of the important, in my opinion, Electron capabilities using the example of my project - shots. Do not worry, the application, the creation of which I described above, can be found on the link


More Electron features using the example --shots


Agree, it is not very convenient to enter the menu every time for the tools that you often use (crop, arrow, etc.). This problem can be solved by creating a panel in the area of ​​the page on which to place them. Already more convenient (no need to wade through the cumbersome menu), but still not perfect (something to be pulled).


Is it better?


Sure you may! Let's use the context menu. It will cause a click of the right mouse button in the window. Such a menu will consist of items, each of which, when pressed, will call its own function. For example, I placed the following items in this menu:



This set of tools will take advantage of almost all the features of the application.


Or maybe even better?


Yes, you can further simplify access to the capabilities of the application. We will achieve this with the help of hotkeys. Hotkeys are registered using the globalShortcut module and the register method. It is necessary to remember about the differences of keyboards in different OS. For example, listening to Command on Linux and Windows is useless. Therefore, the developers of Electron offer to listen to CommandOrControl.
Hotkey in the standard case consists of a modifier (CommandOrControl, Alt, Option ...) and a key code (0 - 9, A - Z, Space, Tab ...). It is worth noting that it does not have to consist of a combination of two keys. There may be one or three (maybe more, but I did not check). For example, to call a modal window with a list of hotkeys, I use F2, and in order to increase the zoom (zoom in) - CommandOrControl+shift+Plus . I had to add shift to this chain, because otherwise the CommandOrControl+shift+Plus combination is converted to ontrol+shift+= .


Process communication


Most likely, you will encounter the need to transfer a message from one process to another (call me such and such a function, for example). For this there are ipcRenderer and ipcMain. We set ourselves a simple task to figure out how it works. Suppose when you click on an item in the menu, we want to call a certain function that will output an alert to us. In the click handler, we write something like appWindow.webContents.send('show'); . This means the following: when you click on a menu item in the renderer process, an asynchronous message will be sent over the 'show' channel. You can also pass additional arguments, so let our alert deduce the passed argument. We slightly modify the previously written appWindow.webContents.send('show', 'content'); .
Now in the renderer process, we write:


 ipcRenderer.on('show', (event, message) => { alert(message) // Alerts 'content' }); 

As you can see, everything is very simple!


Tray


It is useful that the application does not hang minimized or, worse, was simply open. To solve this problem, there is a tray. And Electron gives you the opportunity to work with him through the Tray class.
What essentially is an application in the tray? This is an icon and a menu called up by right-clicking. It is interesting to note that the constructor for the Tray class takes exactly one parameter - an icon. If this parameter is missing, an error will be thrown. At first it didn’t surprise me. There is a required parameter in the constructor, if you please, pass it on. A little later, I will tell you why, in the future, the necessity of this parameter seemed strange to me.


Application icon


Let's talk a little more about decorations. How will our application live without an icon? It is added in the same place where we created a new window by appending to the end of the icon: __dirname + '/icon.png'. It is worth noting that for Windows it is recommended to use the icon in * .ico format. Not scary, because you can use os.platform() .


Can my page reach the system?


Yes of course. At the time of this writing, the current version of Electron is 1.4.6, which works with NodeJS 6.5. For example, --shots has the ability to save the file locally. For this, I use fs.writeFile . And each time before saving, I check if there is a directory for screenshots (I called it “--shots”). If it is missing, then just create it.


It is very important to understand that your application on Electron is not just a page in a standard window, but quite a full-fledged application.


Additional features not found in --shots


Naturally, while working on --shots, I didn’t use all the features of Electron.
For example, you can download your application in the AppStore and WindowsStore. But --shots was conceived as an application for LInux, so I didn’t particularly fit into all these jungle publishing applications. But I know that in order to put the application in the AppStore, you need to sign it. Every time I build a new version of --shots, I see this warning in the console and cross my fingers so that it doesn’t affect anything further.


Even on the site of Electron, you can find one great item: “Automatic updates”. But I couldn’t screw it to --shots, either. opened the article and saw:


"It’s not a problem.

Simply put, it doesn't work on LInux, so get out. For example, I make a request to the server, which returns me the current version number of the current version and, if it does not match the version of the application, I output a message asking to update it.


Underwater rocks


It seems that everything sounds cool, there must be a catch. Of course, there are some unpleasant moments:


Not all cross-platform


This is noticeable even in the example of the same auto-update. With a transparent window is also not so simple. For example, on Linux you need to drive into the command line - --enable-transparent-visuals --disable-gpu , otherwise you will not see any transparent window. Agree, the application, which after installation asks the user to drive something into the terminal, is already beginning to cause suspicion.


Minimize


I spent a lot of time resolving this issue. Its essence is as follows: when we minimize the application window, the minimize event is triggered and the window is minimized. It seems everything is okay. But the application that hangs in the tray and in the dock is already strange. Therefore, I would like to catch minimize and somehow remove the application from the dock, leaving only the tray. For this, there is a hide () method. We write the handler for minimize, call hide - everything is fine. Then I wanted to add the ability to deploy the application back from the tray menu, and immediately found the show () method. Everything is logical show / hide, but no. When I try to deploy an application from the tray, it hangs tight. For a very long time I thought I was missing something important, but I did not see any errors in the console. Including, the window itself was displayed before calling show () - the window exists. In general, I refused the similar mechanism of the application and decided to try to call hide () when the application window is open. And yes, the miracle happened. Everything worked exactly as it should. A few hours were spent on finding the answer to the question: “Why don't you work?”, But in the end the decision was given to me by Telegram. I just added the “minimize to tray” item in the menu, and at the same time I hung it on it.


In general, for me, Electron's pitfalls are over. Most likely, I was just lucky ...


Assembly


It's time to talk about one more interesting thing - the assembly. Electron gives you the opportunity to build your application and then install it in various operating systems. The user may not even guess that you wrote your application on web-technologies. Let's get started!


Standard approach


On the official website in the documentation section there are 3 links to build instructions (Linux, MacOS, WIndows). Since the application was originally sharpened under Linux, I first opened the link for it. The first system requirement immediately scared me: “At least 25GB disk space and 8GB RAM” (at least 25GB of free space and 8GB RAM). “Okay, what to do?” - I thought and began to try to build an application from the example. As a result, the assembly process itself took me 20-30 minutes, moreover, it was impossible to use a computer! I am very glad that I did not continue to work with this builder, since I learned about one most unpleasant thing: in order to build an application for some axis, it must be assembled from under this axis!


“There must be another way,” I said to myself. And yes, indeed it exists.


Electron builder


It was enough just to google “electron builder” and follow the first link. Ever since I use this collector, and here's why:



All you have to do is to complement your project correctly.


Project preparation


This process can be divided into 2 stages:



Icons

Applications, being installed, want to have their own icons. To do this, you need to create a “build” directory in the root directory of the project, which you should fill in as follows:


- build
—— icons
——— 32x32.png
——— 32x32.png.ico
—— icon.icns
—— icon.ico


Package.json

If you just try to build your project, the console will spit at you with errors. This happens for the reason that the file should contain standard lines:



Then you need to describe the "build" and "scripts". I deliberately quickly ran through these two points, because remembering what is inside is not required, and hardly possible, especially considering that there are a huge number of settings (for “build”), which may depend on the OS to which it is going. For example, for Windows, you can build a portable version of your application and specify a gif-file that will be displayed at the time of launch.


In general, I recommend using Electron builder for assembly. It will save you a lot of nerves, believe me.


Add your application to the site Electron


I consider it a good idea to tell about your project on Electron on the site of Electron itself. To do this, it is enough to send them a pull request containing:



Again, this is no big deal. After sending the pr, you only need to be patient and wait for it to be accepted (in my case it took about a week).
Detailed instructions for adding can be found here .


Summarizing


No, Electron will not oust the natives from the market, just as React Native will not kill Swift. Electron is suitable for creating simple applications and is quite an interesting project. And he's damn simple! I managed to write --shots using a standard stack of frontend-developer technologies:



The first commit was made on September 24, 2016, and the first release was released on October 17 of the same year. This means that after only 24 days, I received a version of the application that could already be used. This is fast enough, given that I work and like to relax. So go ahead, everything is in your hands!


useful links



')

Source: https://habr.com/ru/post/316880/


All Articles