How to make friends Electron and Webix

Introduction


Good time of day! I would like to share with you my personal experience in creating a desktop JavaScript application using a combination of Electron and Webix. Such a bundle allows you to speed up the process of layout of the interface, especially without wasting time on layout and other web things that the Webix framework can do.

Tools


So let's get started, we will need the following tools:

  1. An editor in which we will write our code directly. I will use visual studio code (VSC), which can be taken from here ;
  2. Node.js server, which can be taken from here . Download it and install;
  3. The Webix Framework is a free version (Webix Standard is a free UI library under GNU GPLv3 license), which we take from here webix.com/get-webix-gpl . In order to download it, you need to go to the above link, enter an email, first and last name, put three checkmarks, click send, and then a download link will be sent to your mail.

In general, that’s all that we need.

Install the necessary tools


So, we proceed directly to the creation:

1. Install "node.js". I won’t stop here. I’m sure you can do this without me.

2. Create a folder in which we will write our code - for example electron_webix (Fig. 1).


Fig. 1 - Creating a working folder

3. Launch VSC and open this folder (Fig. 2).


Fig. 2 - Open the working folder

4. Open the VSC terminal with the "Ctr +` "key combination and enter the" npm init "command, which will initialize our" node.js "project. After which the system will ask for a bunch of different and very useful questions, which are not required to be answered. Then we press all the time confidently on the “Enter” button and don’t think about anything bad (Fig. 3).


Fig. 3 - Initialization of the project

5. Install directly Electron itself. Why do we enter the command “npm install --save-dev electron” in the VSC console, then we sit and wait until everything is installed (Fig. 4).


Fig. 4 - Installation of Electron

Workspace organization


Now we turn to the organization of the workspace. The first thing we need to do is create several folders and files that we can’t do without (Figure 5):

  • the “libs” folder, here we put the files downloaded from the “Webix” site;
  • the “.vscode” folder, here we put the json file that will run our application in VSC;
  • file "main.js", the main file that will run our Electron application;
  • the “index.html” file will contain our content and markup;
  • file "renderer.js", the event handler of our application. Works in conjunction with index.html;
  • file “launch.json”, in the folder “.vscode” the file that is needed to run our code in the “VSC” environment using the “F5” button;
  • file "style.css", in this file we still have to register some styles so that our window looks decent.


Fig. 5 - Required folders and files

Fill the workspace with initial content


Let's start filling the workspace with the “launch.json” file, which is used to launch our application in the “VSC” environment without any additional commands from the console.
The contents of the “launch.json” file were taken from here . Here we will not delve into the contents of this file, just copy it and save it. The contents of the file are presented below (Fig. 6).

  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Main Process",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
      "windows": {
        "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
      },
      "args" : ["."],
      "outputCapture": "std"
    }
  ]
}



Fig. 6 - The “launch.json” file in the “VSC” environment

Next, we need to correct the “package.json” file, which was automatically created when the “npm init” command was run. It needs to be changed in two lines as shown below (Fig. 7):

There was such a “package.json”:

  "name": "electron_webix",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "electron": "^8.2.3"
  }
}

Here is a package.json:

{
  "name": "electron_webix",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
   "start": "electron ."
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "electron": "^8.2.3"
  }
}


Fig. 7 - The “package.json” file in the “VSC” environment

Now we turn to filling the “main.js” file, copy its contents from here . Insert and save.

const { app, BrowserWindow } = require('electron')

function createWindow () {
  //   .
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // and load the index.html of the app.
  win.loadFile('index.html')

  //   .
  win.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
//  API       .
app.whenReady().then(createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  //       macOS    
  //    ,       Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
   //  MacOS     ,
   //  ,           .
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code.            require.


Fig. 8 - The “main.js” file in the “VSC” environment

After which we can finally finally perform the first launch of the “F5 key” of our application. So, press “F5” and here it is our window (Fig. 9).


Fig. 9 - File “main.js” in the “VSC” environment.

What we see in Fig. 9? This is the working area of ​​our application on the left, and debugging tools on the right.

Now create the initial markup in the index.html file. Why do the following in “VSC”: open the file “index.html” => enter “!” => and click on "Enter" => and then the magic and the following result:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

Well, as in the best tradition of writing code between the "" tags, insert the text "Hello world !!!"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    Hello world!!!
</body>
</html>

After which we try to press “F5” again and check that everything works for us (Fig. 10)


Fig. 10 - “Hello world !!!” window

Now we need to extract the entire contents of the “codebase” folder from the webix.zip archive that we downloaded and transfer it to our libs folder (Fig. 11).

Open the archive, go to the codebase folder, take its contents and transfer it to the libs pack.


Fig. 11 - Contents of the “libs” folder

After filling the contents of the “libs” folder, open the “renderer.js” file and write the following in it:

//   
const { remote } = require('electron')
//           
let WIN = remote.getCurrentWindow()
//   webix
const webix = require('./libs/webix.min.js')
//  JQuery
$ = require('jquery')
// ,              
webix.ui({
})

Next, we are going to typesetting our interface. Why go here designer.webix.com/welcome click on the button "Quick Start" and forward to the layout of the interface. for example, such as shown in Figure 12.


Fig. 12 - Layout of the interface in Webix Web Designer The layout

shown in Figure 12 is very simple and consists in dragging and dropping the desired element into the designer workspace. The right part of the constructor is for editing the properties of elements. When the layout is completed (Fig. 13), you need to transfer it to the renderer.js file. Why click on the “Code” button and get a ready layout in the form of text. Select this text and copy it.


Fig. 13 - Convert layout to text

After that we open the “renderer.js” file in “VSC” and paste the copied text into the prepared “webix.ui” function (Fig. 14).

//   
const { remote } = require('electron')
//           
let WIN = remote.getCurrentWindow()
//   webix
const webix = require('./libs/webix.min.js')
//  JQuery
$ = require('jquery')
//               
webix.ui(
    {
        "id": 1587908357897,
        "rows": [
            {
                "css": "webix_dark",
                "view": "toolbar",
                "height": 0,
                "cols": [
                    { "view": "label", "label": "Elcetron +Webix its cool!"},
                    { "label": "-", "view": "button", "height": 38, "width": 40},
                    { "label": "+", "view": "button", "height": 38, "width": 40},
                    { "label": "x", "view": "button", "height": 38, "width": 40}
                ]
            },
            {
                "width": 0,
                "height": 0,
                "cols": [
                    { "url": "demo->5ea58f0e73f4cf00126e3769", "view": "sidebar", "width": 177 },
                    {
                        "width": 0,
                        "height": 0,
                        "rows": [
                            { "template": "Hello WORLD! ", "view": "template" },
                            {
                                "url": "demo->5ea58f0e73f4cf00126e376d",
                                "type": "bar",
                                "xAxis": "#value#",
                                "yAxis": {},
                                "view": "chart"
                            }
                        ]
                    }
                ]
            }
        ]
    }
)


Fig. 14 - Adding a layout created with the designer

Suffer little more. Next, take and open in “VSC” our pre-prepared index.html file and append there literally the following three lines:, and the
index.html file will take the following form:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./libs/webix.css">
    <link rel="stylesheet" href="./style.css">
    <title>Document</title>
</head>
<body>
    <script src="./renderer.js"></script>
</body>
</html>

Next, click "F5" and get our application with layout from Webix (Fig. 15)


Fig. 15 - The working window of the application with layout from Webix

Add a couple of chips.


Everything seems to work. But I would like to remove the window frame that we don’t need, which doesn’t really fit into our design. We will use a custom solution with you. Why do we open the “main.js” file and add the frame parameter there: false when creating BrowserWindow, this flag removes the standard menu and window border. It should look like this:

const win = new BrowserWindow({
    width: 800,
    height: 600,
    frame:false,
    webPreferences: {
      nodeIntegration: true
    }
  })

Click on “F5” and look at the result without a frame window (Fig. 16).


Fig. 16 - The working window of the application with layout from Webix It

remains to teach our window to respond to the event from the mouse. In the beginning, we will make the title active so that it can move our window around the monitor screen. Why open the “renderer.js” file, find the view: label element in it and add the css: ”head_win” property to it should be as shown in Figure 17.

   { "view": "label", "label": "Elcetron +Webix its cool!", css:"head_win" },
                    { "label": "-", "view": "button", "height": 38, "width": 40},
                    { "label": "+", "view": "button", "height": 38, "width": 40 },
                    { "label": "x", "view": "button", "height": 38, "width": 40}


Fig. 17 - Adding a style to the view: label element

Now you need to register this style in a file created specifically for this purpose. Open the file “style.css” and create the following style:

.head_win {
    -webkit-app-region: drag;
}

After that we launch the “F5” application and try to drag our window by the title. Everything should work.

Finally, our application will respond to clicking on the title buttons of the “-, +, x” window. Why add the following code to the end of the renderer.js file:

//  
$$("close-bt").attachEvent("onItemClick", () => {
    const window = remote.getCurrentWindow();
    window.close();
})

//  
$$("min-bt").attachEvent("onItemClick", () => {
    const window = remote.getCurrentWindow();
    window.minimize();
})

//  
$$("max-bt").attachEvent("onItemClick", () => {
    const window = remote.getCurrentWindow();
    if (!window.isMaximized()) {
        window.maximize();
    } else {
        window.unmaximize();
    }
})

In this code, an entry of the form “$$ (“ max-bt ”)” means access to the “Webix” element by its “id”. Therefore, it is necessary for the header buttons to specify these id in the file "renderer.js", which we will do should be as shown in Figure 18:

   { "view": "label", "label": "Elcetron +Webix its cool!", css:"head_win" },
                    { "label": "-", "view": "button", "height": 38, "width": 40, id:"min-bt" },
                    { "label": "+", "view": "button", "height": 38, "width": 40, id:"max-bt" },
                    { "label": "x", "view": "button", "height": 38, "width": 40, id:"close-bt" }


Fig. 18 - Adding a style to the view: label element

That's all for now. Try it should work. The source code can be downloaded from the github, here is the link . Thank you for attention.

All Articles