Введение


Доброе время суток! Хотелось поделиться с Вами личным опытом создания десктопного приложения на JavaScript с использованием связки Electron и Webix. Такая связка позволяет ускорить процесс верстки интерфейса, особо не тратя время на разметку и прочие web штуки, которыми может заняться как раз фреймворк Webix.

Инструменты


Итак приступим, на понадобится следующие инструменты:

  1. Редактор, в котором мы будем писать непосредственно наш код. Я буду использовать visual studio code (VSC), который можно взять отсюда ;
  2. Сервер Node.js, который можно взять отсюда . Скачиваем его и устанавливаем;
  3. Фреймворк «Webix» бесплатную версию (Webix Standard is a free UI library under GNU GPLv3 license), которую берем вот отсюда webix.com/get-webix-gpl. Для того что бы его скачать нужно перейти по выше приведенной ссылке, вести email, имя и фамилию, поставить три галочки нажать отправить после чего Вам на почту отправят ссылку для скачивания.

В общем то вот и все, что нам с Вами потребуется.

Устанавливаем необходимые инструменты


Итак, переходим непосредственно к созданию:

1. Устанавливаем «node.js». Тут я останавливаться не буду я уверен с этим Вы справитесь без меня.

2. Создаем папку, в которой будем писать наш код — например electron_webix (рис. 1).


Рис. 1 – Создание рабочей папки

3. Запускаем VSC и открываем эту папку (рис. 2).


Рис. 2 – Открываем рабочую папку

4. Открываем в VSC терминал комбинацией клавиш «Ctr+`» и вводим команду «npm init», которая произведет инициализацию нашего «node.js» проекта. После чего система запросит кучу разных и очень полезных вопросов, на которые отвечать не обязательно. Далее жмем все время уверенно на кнопку «Enter» и не думаем не о чем плохом (рис. 3).


Рис. 3 – Инициализация проекта

5. Устанавливаем непосредственно сам «Electron». Для чего в консоли VSC вводим команду «npm install --save-dev electron», после сидим и ждем пока все установиться (рис. 4).


Рис. 4 – Установка «Electron»

Организация рабочего пространства


Теперь переходим к организации рабочее пространство. Первое что мы должны сделать это создать несколько папок и файлов, без которых не обойтись (рис. 5):

  • папку «libs», сюда положим файлы скаченные с сайта «Webix»;
  • папку «.vscode», сюда положим json файл, который будет запускать наше приложение в VSC;
  • файл «main.js», основной файл, который будет запускать наше приложения «Electron»;
  • файл «index.html», будет содержать наш контент и разметку;
  • файл «renderer.js», обработчик событий нашего приложения. Работает в связке с index.html;
  • файл «launch.json», в папке «.vscode» файл который нужен для запуска нашего кода в среде «VSC» кнопочкой «F5»;
  • файл «style.css», в этом файле нам все таки придется прописать кое-какие стили что бы наше окошко выглядело по приличней.


Рис. 5 – Необходимые папки и файлы

Наполняем рабочее пространство первоначальным содержимым


Начнем наполнение рабочего пространства с файла «launch.json», который используется для запуска нашего приложения в среде «VSC» без всяких дополнительных команд из консоли.
Содержимое файла «launch.json» было взято вот отсюда. Тут мы не будем вникать в содержимое данного файла просто его скопируем и сохраним. Содержимое файла представлено ниже (рис. 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"
    }
  ]
}



Рис. 6 – Файл «launch.json» в среде «VSC»

Далее нам необходимо подправить немного файл «package.json», который был автоматически создан при выполнении команды «npm init». В нем нужно произвести изменение в двух строках как показано ниже (рис. 7):

Был вот такой «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"
  }
}

Стал вот такой «package.json»:

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


Рис. 7 – Файл «package.json» в среде «VSC»

Теперь переходим к наполнению файла «main.js», его содержимое копируем вот отсюда. Вставляем и сохраняем.

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.


Рис. 8 – Файл «main.js» в среде «VSC»

После чего мы уже можем наконец-то выполнить первый запуск «клавиша F5» нашего приложения. Итак, жмем «F5» и вот оно наше окошко (рис. 9).


Рис. 9 – Файл «main.js» в среде «VSC»

Что мы видим на рис. 9? Это слева рабочая область нашего приложения, а справа средства отладки приложения.

Теперь создадим первоначальную разметку в файле «index.html». Для чего в «VSC» делаем следующие: открываем файл «index.html»=> вводим «!» => и жмем на «Enter»=> а дальше магия и следующий результат:

<!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>

Ну и как в лучших традициях написания кода между тегами «» вставим текст «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>

После чего пробуем нажать опять «F5» и проверяем, что у нас все работает (рис. 10)


Рис. 10 – Окно «Hello world!!!»

Теперь нужно из скаченного нами архива webix.zip вытащить все содержимое папки «codebase» и перенести его в нашу папку libs (рис. 11).

Открываем архив заходим в папку codebase берем ее содержимое и переносим в паку libs.


Рис. 11 – Содержимое папки «libs»

После того как наполнили содержимым папку «libs», открываем файл «renderer.js» и в нем пишем следующее:

// Подключаем модуль электрона
const { remote } = require('electron')
// Получаем указатель на окно браузера где будет производиться отображение нашего интерфейса
let WIN = remote.getCurrentWindow()
// Подключаем фреймворк webix
const webix = require('./libs/webix.min.js')
//Подключаем фреймворк JQuery
$ = require('jquery')
// Функция, в которую в качестве параметра как раз и будет передаваться наша с Вами верстка
webix.ui({
})

Далее приступаем к верстки нашего интерфейса. Для чего переходим вот сюда designer.webix.com/welcome жмем на кнопку «Quick Start» и вперед к верстке интерфейса. например такого какой показан на рисунке 12.


Рис. 12 – Верстка интерфейса в Web конструкторе Webix

Верстка, показанная на рисунке 12 очень проста, и заключается в перетаскивании мышкой нужного элемента в рабочую область конструктора. Правая часть конструктора предназначена для редактирования свойств элементов. Когда верстка закончена (рис. 13) необходимо ее перенести в файл renderer.js. Для чего жмем на кнопку «Code» и получаем готовую верстку в виде текста. Выделяем этот текст и копируем его.


Рис. 13 – Преобразование верстки в текст

После чего открываем файл «renderer.js» в «VSC» и вставляем в заранее подготовленную функцию «webix.ui» скопированный текст (рис. 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"
                            }
                        ]
                    }
                ]
            }
        ]
    }
)


Рис. 14 – Добавление верстки созданной с помощью конструктора

Потерпите еще немного. Далее берем и открываем в «VSC» наш заранее подготовленный файл index.html и дописываем туда буквально три следующие строки: , и
Файл index.html примет следующий вид:

<!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>

Далее жмем «F5» и получаем наше приложение с версткой от Webix (рис. 15)


Рис. 15 – Рабочее окно приложения с версткой от Webix

Добавляем пару фишек


Все вроде работает. Но хотелось бы убрать не нужное нам с вами обрамление окна, которое не очень то вписывается в наш дизайн. Мы будем использовать с вами кастомное решение. Для чего открываем файл «main.js» и добавляем туда параметр frame:false при создании BrowserWindow, данный флаг убирает стандартное меню и обрамление окна. Должно получиться вот так:

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

Жмем на «F5» смотрим на результат без рамочного окна (рис. 16).


Рис. 16 – Рабочее окно приложения с версткой от Webix

Осталось научить наше окно реагировать на событие от мышки. В начале сделаем заголовок активным, чтобы за него можно было перемещать наше окно по экрану монитора. Для чего открываем файл «renderer.js» находим в нем элемент view:label и добавляем в него свойство css:”head_win” должно получиться как показано на рисунке 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}


Рис. 17 – Добавление стиля к элементу view:label

Теперь необходимо собственно этот стиль прописать в файле созданном специально для этих целей. Открываем файл «style.css» и создаем следующий стиль:

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

После чего запускаем приложение «F5» и пробуем перетащить наше окно за заголовок. Должно все работать.

Напоследок наше приложение реагировать на нажатие мышкой на кнопки заголовка «-,+,x» окна. Для чего в конец файла «renderer.js» добавим следующий код:

// Закрытие окна
$$("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();
    }
})

В этом коде запись вида «$$(“max-bt”)» означает, обращение к элементу «Webix» по его «id». Поэтому необходимо для кнопок заголовка эти id прописать в файле «renderer.js», что мы и сделаем должно получиться как показано на рисунке 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" }


Рис. 18 – Добавление стиля к элементу view:label

На этом пока все. Пробуйте должно работать. Исходный код можно скачать с гитхаба, вот ссылка. Благодарю за внимание.