Статьи

Создание настольного приложения с помощью Electron

Как и что из настольных приложений JavaScript

Настольные приложения всегда занимали особое место в моем сердце. С тех пор, как браузеры и мобильные устройства стали мощными, настольные приложения постепенно заменяются мобильными и веб-приложениями. Тем не менее, есть много положительных сторон при написании настольных приложений — они всегда присутствуют, как только они находятся в вашем меню «Пуск» или на док-станции, они могут быть  активированы при помощи alt (cmd)  (надеюсь, это слово) и в основном лучше соединяются с основная операционная система (с ее ярлыками, уведомлениями и т. д.), чем веб-приложения.

Github ElectronВ этой статье я постараюсь провести вас через процесс создания простого настольного приложения и затронуть важные концепции создания настольного приложения с помощью JavaScript.

Основная идея разработки приложений с использованием JavaScript состоит в том, что вы создаете одну кодовую базу и упаковываете ее для каждой операционной системы. Это абстрагирует знания, необходимые для создания собственных настольных приложений, и упрощает обслуживание. В настоящее время разработки настольных приложений с JavaScript использует либо  Electron  или  NW.js . Оба инструмента предлагают более или менее одинаковые функции, я пошел с Electron, потому что у него есть  некоторые преимущества, которые я считаю важными .

В конце дня вы не ошибетесь ни с одним из них.

Основные предположения

Я предполагаю, что у вас есть ваш основной текстовый редактор (или IDE) и  Node.js / npm . Я также предполагаю, что у вас есть знания HTML / CSS / JavaScript (знание Node.js с модулями CommonJS было бы здорово, но не важно), поэтому мы можем сосредоточиться на изучении концепций Electron, не беспокоясь о создании пользовательского интерфейса (который Оказывается, это просто обычные веб-страницы). Если нет, то вы, вероятно, почувствуете себя немного растерянным, и я рекомендую посетить  мой предыдущий пост  в блоге, чтобы освежить в себе основы.

10 000-футовый вид электрона

Короче говоря, Electron предоставляет среду выполнения для создания настольных приложений с использованием чистого JavaScript. Это работает так: Electron берет  основной  файл, определенный в вашем   файле package.json, и выполняет его. Этот основной файл (обычно называемый  main.js ) затем создает окна приложений, которые содержат отображаемые веб-страницы с дополнительной возможностью взаимодействия с собственным графическим интерфейсом (графическим интерфейсом пользователя) вашей операционной системы.

Подробно, как только вы запускаете приложение, используя Electron, создается  основной процесс  . Этот  основной процесс  отвечает за взаимодействие с собственным графическим интерфейсом вашей операционной системы и создает графический интерфейс вашего приложения (окна вашего приложения).

Чистый запуск  основного процесса  не дает пользователям вашего приложения никаких окон приложений. Они создаются  основным процессом  в главном файле с помощью   модуля BrowserWindow . Затем каждое окно браузера запускает свой собственный  процесс рендеринга . Этот  процесс рендеринга  берет веб-страницу (HTML-файл, который ссылается на обычные CSS-файлы, файлы JavaScript, изображения и т. Д.) И отображает ее в окне.

Например, если у вас есть только приложение калькулятора, ваш  основной процесс  будет создавать окно с веб-страницей, где находится ваша фактическая веб-страница (калькулятор).

Хотя говорится, что только  основной процесс  взаимодействует с собственным графическим интерфейсом вашей операционной системы, существуют методы, позволяющие переложить часть этой работы на  процессы визуализации ( мы рассмотрим создание функции, использующей такую ​​технику ).

Основной процесс  может получить доступ к родной GUI через ряд модулей  , доступных непосредственно в Electron . Ваше настольное приложение может получить доступ ко всем модулям Node, например, к отличному  узлу-уведомителю  для отображения системных уведомлений,  запросов  на выполнение HTTP-вызовов и т. Д.

Привет мир!

Давайте начнем с традиционного приветствия и установим все необходимые предпосылки.

Сопутствующий репозиторий

Это руководство сопровождается  репозиторием звуковой машины и учебника .
Используйте репозиторий, чтобы следовать или продолжить в определенных точках. Клонируйте репозиторий, чтобы начать:

git clone https://github.com/bojzi/sound-machine-electron-guide.git

и затем вы можете перейти к тегу git в   папке sound-machine-tutorial с помощью:

git checkout <tag-name>

Я дам вам знать, когда будет доступен тег с блоком кода:

Follow along:
git checkout 00-blank-repository

Как только вы клонируете / извлекаете нужный тег, запустите:

npm install

так что вы не пропустите ни одного модуля Node.

Если вы не можете переключиться на другой тег, было бы проще просто сбросить состояние вашего хранилища и затем выполнить проверку:

git add -A
git reset --hard

Открыть магазин

Follow along with the tag 00-blank-repository:
git checkout 00-blank-repository

В папке проекта создайте в ней новый   файл package.json со следующим содержимым:

{
    "name": "sound_machine",
    "version": "0.1.0",
    "main": "./main.js",
    "scripts": {
        "start": "electron ."
    }
}

Это barebones  package.json:

  • Устанавливает название и версию приложения,
  • Позволяет Electron знать, какой скрипт   будет запускать основной процесс ( main.js ) и
  • Устанавливает полезный ярлык —   скрипт npm, чтобы легко запустить приложение, запустив « npm start » в вашем CLI (терминале или командной строке).

Пришло время получить  Электрон . Самый простой способ сделать это — установить предварительно собранный двоичный файл для вашей операционной системы через  npm  и сохранить его как зависимость для разработки в вашем package.json ( это происходит автоматически с  –save-dev) . Запустите следующее в вашем CLI (в папке проекта):

npm install --save-dev electron-prebuilt

Готовый двоичный файл предназначен для операционной системы, в которой он установлен, и позволяет запускать « npm start ». Мы устанавливаем его как зависимость для разработки, потому что он нам понадобится только во время разработки.

Это более или менее все, что вам нужно, чтобы начать разработку с  Electron .

Приветствие Мира

Создайте   папку приложения и   файл index.html в этой папке со следующим содержимым:

<h1>Hello, world!</h1>

В корне проекта создайте  файл main.js. Это файл, который ускорит основной процесс Electron и позволит создать наш «Hello, world!» Веб-страница. Создайте файл со следующим содержимым:

'use strict';

var app = require('app');
var BrowserWindow = require('browser-window');

var mainWindow = null;

app.on('ready', function() {
    mainWindow = new BrowserWindow({
        height: 600,
        width: 800
    });

    mainWindow.loadUrl('file://' + __dirname + '/app/index.html');
});

Ничего страшного, правда?

Модуль  приложения  контролирует жизнь вашего приложения (например, отслеживает, когда ваше приложение готово для создания окон) .

Модуль  BrowserWindow  позволяет создавать окна.

Объект  mainWindow  будет вашим главным окном приложения и будет объявлен как  null, потому что в противном случае окно закроется, как только начнется сборка мусора JavaScripts.

Как только  приложение  получит  готовое  событие, мы создадим новое окно шириной 800 пикселей и высотой 600 пикселей с помощью  BrowserWindow .

Процесс рендеринга этого окна   будет отображать наш   файл index.html .

Запустите наш «Привет, мир!» приложение, запустив в CLI следующее:

npm start

и греться во славе, это ваше приложение.

Привет мир!


Привет действительно.

Разработка реального приложения

Славная звуковая машина

Перво-наперво -  что за звуковая машина ?

Звуковая машина — это маленькое устройство, которое издает звуки при нажатии различных кнопок, в основном, мультяшных или реакционных звуков. Это забавный маленький инструмент для поднятия настроения в офисе и отличный пример использования для разработки настольного приложения, так как мы собираемся исследовать немало концепций при его разработке (и получить изящную звуковую машину для загрузки).

звук машина обучающая @ 2x

Функции, которые мы собираемся построить, и концепции, которые мы собираемся исследовать:

  • Базовая звуковая машина (базовая реализация окна браузера),
  • Закрытие звуковой машины (удаленные сообщения между  основным процессом  и  процессом визуализации),
  • Воспроизведение звуков без фокусировки приложения (глобальные сочетания клавиш),
  • Создание экрана настроек для клавиш-модификаторов горячих клавиш (Shift, Ctrl и Alt) (сохранение пользовательских настроек в домашней папке),
  • Добавление иконки в трее (удаленное создание собственных элементов графического интерфейса и знакомство с меню и иконкой в ​​трее) и
  • Упаковка вашего приложения (упаковка вашего приложения для Mac, Windows и Linux).

Создание основной функции звуковой машины

Начальная точка и организация приложений

С рабочим «Привет, мир!» Приложение под ваш пояс, самое время начать строить звуковую машину.Файлы @ 2x

Типичная звуковая машина имеет несколько рядов кнопок, которые реагируют на нажатия, издавая звуки. Звуки в основном мультяшные и / или основанные на реакции (смех, хлопки, разбитие стекла и т. Д.).

Это также самая первая функция, которую мы создадим — базовая звуковая машина, которая реагирует на щелчки.

Наша структура приложения будет очень простой.

В  корне  приложения мы будем хранить   файл package.json, файл  main.js  и любые другие необходимые для приложения файлы.

В   папке приложения будут храниться наши HTML-файлы различных типов в таких папках, как  cssjswav  и  img .

Чтобы упростить задачу, все файлы, необходимые для дизайна веб-страницы, уже включены в исходное состояние хранилища. Пожалуйста, отметьте тег 01-start-project out. Если вы последовали и создали «Привет, мир!» приложение, вам придется сбросить свой репозиторий, а затем сделать проверку:

If you followed along with the "Hello, world!" example:
git add -A
git reset --hard
Follow along with the tag 01-start-project:
git checkout 01-start-project

Для простоты у нас будет только два звука, но расширение его до полных 16 звуков — это просто вопрос поиска дополнительных звуков, дополнительных значков и изменения  index.html .

Определение остальной части основного процесса

Давайте  вернемся  к main.js, чтобы определить внешний вид звуковой машины. Заменить содержимое файла на:

'use strict';

var app = require('app');
var BrowserWindow = require('browser-window');

var mainWindow = null;

app.on('ready', function() {
    mainWindow = new BrowserWindow({
        frame: false,
        height: 700,
        resizable: false,
        width: 368
    });

    mainWindow.loadUrl('file://' + __dirname + '/app/index.html');
});

Мы настраиваем окно, которое создаем, придавая ему размеры, делая его неизменяемым и безрамным. Это будет похоже на настоящую звуковую машину, зависшую на вашем рабочем столе.

Теперь возникает вопрос — как переместить безрамное окно (без заголовка) и закрыть его?
Я расскажу о закрытии пользовательского окна (и приложения) очень скоро (и представлю способ связи между  основным процессом  и  процессом визуализации ), но часть перетаскивания проста. Если вы посмотрите на   файл index.css (в  app / css ), вы увидите следующее:

html,
body {
    ...
    -webkit-app-region: drag;
    ...
}

-webkit-app-region: перетащить; позволяет всему  HTML  быть перетаскиваемым объектом. Однако сейчас есть проблема — вы не можете нажимать кнопки на перетаскиваемом объекте. Другая часть головоломки —  -webkit-app-region: no-drag; который позволяет вам определить неотступные (и, следовательно, кликабельные элементы). Рассмотрим следующую выдержку из  index.css :

.button-sound {
    ...
    -webkit-app-region: no-drag;
}

Отображение звуковой машины в собственном окне

Main.js  файл теперь может сделать новое окно и отобразить звуковую машину. И действительно, если вы запустите приложение с помощью  npm start,  вы увидите, что звуковая машина оживает. Конечно, сейчас ничего не происходит, потому что у нас просто статическая веб-страница.

Поместите следующее в   файл index.js (расположенный в  app / js ), чтобы обеспечить интерактивность:

'use strict';

var soundButtons = document.querySelectorAll('.button-sound');

for (var i = 0; i < soundButtons.length; i++) {
    var soundButton = soundButtons[i];
    var soundName = soundButton.attributes['data-sound'].value;

    prepareButton(soundButton, soundName);
}

function prepareButton(buttonEl, soundName) {
    buttonEl.querySelector('span').style.backgroundImage = 'url("img/icons/' + soundName + '.png")';

    var audio = new Audio(__dirname + '/wav/' + soundName + '.wav');
    buttonEl.addEventListener('click', function () {
        audio.currentTime = 0;
        audio.play();
    });
}

Этот код довольно прост. Мы:

  • Запрос на кнопки звука,
  • Перебирайте кнопки, считывая   атрибут data-sound ,
  • Добавить фоновое изображение для каждой кнопки,
  • И добавьте событие click для каждой кнопки, воспроизводящей аудио (используя  интерфейс HTMLAudioElement ).

Протестируйте свое приложение, запустив в своем CLI следующее:

npm start

Звуковая машина


Работающий звуковой аппарат!

Закрытие приложения из окна браузера через удаленные события

Follow along with the tag 02-basic-sound-machine:
git checkout 02-basic-sound-machine

Повторим еще раз: окна приложений (точнее их  процесс рендеринга ) не должны взаимодействовать с графическим интерфейсом (и это то, что закрывает окно). Официальный Electron Краткое руководство  говорит:

На веб-страницах нельзя вызывать API-интерфейсы, связанные с собственным графическим интерфейсом, потому что управление ресурсами собственного графического интерфейса на веб-страницах очень опасно и утечка ресурсов проста. Если вы хотите выполнить операции с графическим интерфейсом на веб-странице, процесс визуализации веб-страницы должен связаться с основным процессом, чтобы запросить основной процесс выполнить эти операции.

Electron предоставляет   модуль ipc (межпроцессное взаимодействие)  для этого типа связи.  ipc  позволяет подписываться на сообщения на  канале  и отправлять сообщения подписчикам  канала . Канал используется для различения получателей сообщений и представлен строкой (например, «канал-1», «канал-2»…). Сообщение также может содержать данные. Получив сообщение, абонент может отреагировать, выполнив некоторую работу, и даже может ответить. Самым большим преимуществом обмена сообщениями является разделение задач —  основной процесс не должен знать, какие  существуют процессы визуализации  или какой отправил сообщение.

обмен сообщениями

Это именно то, что мы будем здесь делать — подписать  основной процесс  ( main.js ) на канал « close-main-window » и отправить сообщение по этому  каналу  из  процесса рендерера  ( index.js ), когда кто-то нажимает кнопку закрытия. ,

Добавьте следующее в  main.js,  чтобы подписаться на канал:

var ipc = require('ipc');

ipc.on('close-main-window', function () {
    app.quit();
});

После запроса модуля подписка на сообщения на канале очень проста и включает использование   метода on () с именем канала и функцией обратного вызова.

Чтобы отправить сообщение по этому каналу, добавьте в index.js следующее  :

var ipc = require('ipc');

var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function () {
    ipc.send('close-main-window');
});

Опять же, мы требуем  модуль ipc  и привязываем  событие click  к элементу с помощью кнопки закрытия. При нажатии на кнопку закрытия мы отправляем сообщение через канал «close-main-window»  методом send () .

Есть еще одна деталь, которая может вас укусить, и мы уже говорили об этом —  кликабельность  перетаскиваемых областей. index.css  должен определить кнопку закрытия как не перетаскиваемую.

.settings {
    ...
    -webkit-app-region: no-drag;
}

Вот и все, наше приложение теперь можно закрыть с помощью кнопки закрытия. Общение через  ipc  может усложниться, изучив событие или передав аргументы, и мы увидим пример передачи аргументов позже.

Воспроизведение звуков через глобальные сочетания клавиш

Follow along with the tag 03-closable-sound-machine:
git checkout 03-closable-sound-machine

Наша базовая звуковая машина работает отлично. Но у нас есть проблема юзабилити — какая польза от звуковой машины, которая должна все время сидеть перед всеми вашими окнами и нажиматься несколько раз?

Вот где появляются глобальные сочетания клавиш. Electron предоставляет  глобальный  модуль сочетаний клавиш, который позволяет прослушивать пользовательские комбинации клавиш и реагировать на них. Комбинации клавиш известны как  Ускорители  и являются строковыми представлениями комбинации нажатий клавиш (например, «Ctrl + Shift + 1»).

Горячие клавиши Так как мы хотим , чтобы поймать родное событие GUI (глобальный комбинацию клавиш) и сделать событие окна программы (воспроизведение звука), мы будем использовать наш доверенный  Ipc  модуль для отправки сообщения из  основного процесса  в  процесс рендеринга .

Прежде чем углубиться в код, необходимо рассмотреть две вещи:

  1. Глобальные ярлыки должны быть зарегистрированы после  события «готово» приложения  (код должен быть в этом блоке) и,
  2. При отправке сообщений через  ipc  из  основного процесса  в  процесс рендеринга  вы должны использовать ссылку на это окно (что-то вроде  «madeWindow.webContents.send (‘channel’)» ).

Имея это в виду, давайте изменим наш  main.js  и добавим следующий код:

var globalShortcut = require('global-shortcut');

app.on('ready', function() {
    ... // existing code from earlier

    globalShortcut.register('ctrl+shift+1', function () {
        mainWindow.webContents.send('global-shortcut', 0);
    });
    globalShortcut.register('ctrl+shift+2', function () {
        mainWindow.webContents.send('global-shortcut', 1);
    });
});

Во-первых, нам нужен   модуль global-shortcut . Затем, как только наше приложение будет готово, мы зарегистрируем два ярлыка — один, который будет реагировать на нажатие клавиш Ctrl, Shift и 1 вместе, а другой, который будет реагировать на нажатие клавиш Ctrl, Shift и 2 вместе. Каждый из них отправит сообщение на канал « global-shortcut » с аргументом. Мы будем использовать этот аргумент для воспроизведения правильного звука. Добавьте следующее в  index.js :

ipc.on('global-shortcut', function (arg) {
    var event = new MouseEvent('click');
    soundButtons[arg].dispatchEvent(event);
});

Для простоты мы собираемся смоделировать нажатие кнопки и использовать селектор soundButtons, который мы создали при привязке кнопок к воспроизведению звуков. Как только сообщение приходит с аргументом 1, мы берем  элемент soundButtons [1]  и запускаем щелчок мышью по нему ( примечание : в производственном приложении вы хотите инкапсулировать код воспроизведения звука и выполнить его).

Настройка ключей-модификаторов через настройки пользователя в новом окне

Follow along with the tag 04-global-shortcuts-bound:
git checkout 04-global-shortcuts-bound

Поскольку одновременно запущено так много приложений, вполне возможно, что предусмотренные нами ярлыки уже заняты. Вот почему мы собираемся представить экран настроек и сохранить, какие модификаторы (Ctrl, Alt и / или Shift) мы будем использовать.

Для этого нам понадобится следующее:

  • Кнопка настроек в нашем главном окне,
  • Окно настроек (с сопровождающими файлами HTML, CSS и JavaScript),
  • IPC  сообщения, чтобы открыть и закрыть окно настроек и обновить наши глобальные ярлыки и
  • Сохранение / чтение файла настроек JSON из пользовательской системы.

Фу, это довольно большой список.

Кнопка настроек и окно настроек

Аналогично закрытию главного окна, мы будем отправлять сообщения на  канал  из  index.js, когда нажимается кнопка настроек. Добавьте следующее в  index.js :

var settingsEl = document.querySelector('.settings');
settingsEl.addEventListener('click', function () {
    ipc.send('open-settings-window');
});

После нажатия кнопки настроек на канале « open-settings-window » отправляется сообщение. main.js  теперь может реагировать на это событие и открывать новое окно. Добавьте следующее в  main.js :

var settingsWindow = null;

ipc.on('open-settings-window', function () {
    if (settingsWindow) {
        return;
    }

    settingsWindow = new BrowserWindow({
        frame: false,
        height: 200,
        resizable: false,
        width: 200
    });

    settingsWindow.loadUrl('file://' + __dirname + '/app/settings.html');

    settingsWindow.on('closed', function () {
        settingsWindow = null;
    });
});

Ничего нового, чтобы увидеть здесь, мы открываем новое окно, как мы сделали с главным окном. Разница лишь в том, что мы проверяем, открыто ли окно настроек, чтобы не открывать два экземпляра.

Как только это сработает, нам нужен способ закрыть это окно настроек. Опять же, мы отправим сообщение на канал, но на этот раз из  settings.js  (поскольку там находится кнопка закрытия настроек). Создайте (или замените содержимое)  settings.js  следующим образом:

'use strict';

var ipc = require('ipc');

var closeEl = document.querySelector('.close');
closeEl.addEventListener('click', function (e) {
    ipc.send('close-settings-window');
});

И слушать на этом  канале  в  main.js . Добавьте следующее:

ipc.on('close-settings-window', function () {
    if (settingsWindow) {
        settingsWindow.close();
    }
});

Теперь наше окно настроек готово к реализации собственной логики.

Хранение и чтение пользовательских настроек

Follow along with the tag 05-settings-window-working:
git checkout 05-settings-window-working

Процесс взаимодействия с окнами настроек, сохранения настроек и продвижения их в наше приложение будет выглядеть следующим образом:

  • создать способ хранения и чтения пользовательских настроек в файле JSON,
  • используйте эти настройки для отображения начального состояния окна настроек,
  • обновить настройки при взаимодействии с пользователем и
  • сообщите  основному процессу  об изменениях.

Мы могли бы просто реализовать сохранение и чтение настроек в нашем  файле main.js,  но это звучит как отличный вариант использования для написания небольшого модуля, который мы затем можем включить в различные места.

Работа с конфигурацией JSON 

Вот почему мы собираемся создать  файл configuration.js  и запрашивать его всякий раз, когда нам это нужно. Node.js использует  шаблон модуля CommonJS,  что означает, что вы экспортируете только свой API, а другие файлы требуют / используют функции, доступные в этом API.

конфигурация

Чтобы упростить хранение и чтение, мы будем использовать  модуль nconf,  который абстрагирует чтение и запись файла JSON для нас. Это отлично подходит. Но сначала мы должны включить его в проект с помощью следующей команды, выполняемой в CLI:

npm install --save nconf

Это говорит npm установить  модуль nconf  как зависимость приложения, и он будет включен и использован, когда мы упаковываем наше приложение для конечного пользователя (в отличие от установки с аргументом save-dev,  который будет включать только модули для целей разработки).

Configuration.js  файл является довольно простой, поэтому давайте полностью изучить его. Создайте   файл configuration.js в корне проекта со следующим содержимым:

'use strict';

var nconf = require('nconf').file({file: getUserHome() + '/sound-machine-config.json'});

function saveSettings(settingKey, settingValue) {
    nconf.set(settingKey, settingValue);
    nconf.save();
}

function readSettings(settingKey) {
    nconf.load();
    return nconf.get(settingKey);
}

function getUserHome() {
    return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}

module.exports = {
    saveSettings: saveSettings,
    readSettings: readSettings
};

nconf  хочет знать, где хранить ваши настройки, и мы даем ему местоположение домашней папки пользователя и имя файла. Получение домашней папки пользователя — это просто вопрос Node.js ( process.env ) и различие между различными платформами (как это наблюдается в функции getUserHome ()  ).

Сохранение или чтение настроек затем выполняется с помощью встроенных методов  nconf  ( set ()  для хранения,  get ()  для чтения с помощью  save ()  и  load ()  для файловых операций) и экспорта API с помощью стандартного модуля CommonJS  .  Синтаксис экспорта .

Инициализация модификаторов сочетаний клавиш по умолчанию

Прежде чем перейти к взаимодействию с настройками, давайте инициализируем настройки, если мы запускаем приложение в первый раз. Мы будем хранить клавиши , как массив с ключом « shortcutKeys » и инициализирует его в  main.js . Чтобы все это работало, мы должны сначала потребовать наш   модуль конфигурации :

'use strict';

var configuration = require('./configuration');

app.on('ready', function () {
    if (!configuration.readSettings('shortcutKeys')) {
        configuration.saveSettings('shortcutKeys', ['ctrl', 'shift']);
    }
    ...
}

Мы пытаемся читать, если что-то хранится под установочной клавишей « горячие клавиши ». Если нет, мы устанавливаем начальное значение.

В качестве дополнительной вещи в  main.js мы перепишем регистрацию глобальных горячих клавиш как функцию, которую мы можем вызвать позже, когда мы обновим наши настройки. Удалите регистрацию сочетаний клавиш из  main.js  и измените файл следующим образом:

app.on('ready', function () {
    ...
    setGlobalShortcuts();
}

function setGlobalShortcuts() {
    globalShortcut.unregisterAll();

    var shortcutKeysSetting = configuration.readSettings('shortcutKeys');
    var shortcutPrefix = shortcutKeysSetting.length === 0 ? '' : shortcutKeysSetting.join('+') + '+';

    globalShortcut.register(shortcutPrefix + '1', function () {
        mainWindow.webContents.send('global-shortcut', 0);
    });
    globalShortcut.register(shortcutPrefix + '2', function () {
        mainWindow.webContents.send('global-shortcut', 1);
    });
}

Функция сбрасывает глобальные ярлыки, так что мы можем устанавливать новые, считывать массив ключей модификаторов из настроек, преобразовывать его в совместимую с Accelerator  строку и выполнять  обычную глобальную регистрацию сочетаний клавиш.

Взаимодействие в окне настроек

Вернувшись в   файл settings.js , нам нужно связать события кликов, которые изменят наши глобальные ярлыки. Сначала мы пройдемся по флажкам и отметим активные (считывая значения из модуля конфигурации):

var configuration = require('../configuration.js');

var modifierCheckboxes = document.querySelectorAll('.global-shortcut');

for (var i = 0; i < modifierCheckboxes.length; i++) {
    var shortcutKeys = configuration.readSettings('shortcutKeys');
    var modifierKey = modifierCheckboxes[i].attributes['data-modifier-key'].value;
    modifierCheckboxes[i].checked = shortcutKeys.indexOf(modifierKey) !== -1;

... // Binding of clicks comes here
}

А теперь мы свяжем поведение флажка. Учтите, что окну настроек (и процессу его  визуализации)  не разрешено изменять привязку графического интерфейса. Это означает, что нам нужно отправить  ipc-  сообщение из  settings.js  (и обработать это сообщение позже) :

for (var i = 0; i < modifierCheckboxes.length; i++) {
...

    modifierCheckboxes[i].addEventListener('click', function (e) {
        bindModifierCheckboxes(e);
    });
}

function bindModifierCheckboxes(e) {
    var shortcutKeys = configuration.readSettings('shortcutKeys');
    var modifierKey = e.target.attributes['data-modifier-key'].value;

    if (shortcutKeys.indexOf(modifierKey) !== -1) {
        var shortcutKeyIndex = shortcutKeys.indexOf(modifierKey);
        shortcutKeys.splice(shortcutKeyIndex, 1);
    }
    else {
        shortcutKeys.push(modifierKey);
    }

    configuration.saveSettings('shortcutKeys', shortcutKeys);
    ipc.send('set-global-shortcuts');
}

Это большой кусок кода, но все еще довольно простой.

Мы перебираем все флажки, связываем событие щелчка и при каждом щелчке проверяем, содержит ли массив настроек ключ модификатора или нет — и в соответствии с этим результатом модифицируем массив, сохраняем результат в настройках и отправляем сообщение  основному процессу  который должен обновить наши глобальные ярлыки.

Все, что осталось сделать, это подписаться на   канал ipc « set-global-shortcuts » в  main.js  и обновить наши глобальные ярлыки:

ipc.on('set-global-shortcuts', function () {
    setGlobalShortcuts();
});

Легко. И с этим наши глобальные сочетания клавиш настраиваются!

Что в меню?

Follow along with the tag 06-shortcuts-configurable:
git checkout 06-shortcuts-configurable

Другой важной концепцией в настольных приложениях являются меню. Это всегда полезное контекстное меню (контекстное меню AKA), меню в трее (привязано к значку в трее), меню приложений (в OS X) и т. Д.

Меню В этом руководстве мы добавим иконку в трее с меню. Мы будем также использовать эту возможность , чтобы исследовать другой способ связи между процессами —  в  удаленный  модуль .

Удаленный  модуль делает RPC вызовов из  процесса визуализатора  к  основному процессу . На практике это означает, что вы дистанционно запрашиваете модули GUI из  main.js  и вызываете методы для них. Таким образом, вы можете запросить объект BrowserWindow из  основного процесса  и создать новое окно браузера в окне ( процесс визуализации ). За кулисами это все еще синхронное   сообщение ipc, но оно предоставляет очень хороший инструмент для продвижения организации в вашем коде.

Дистанционный пульт

Давайте посмотрим, как мы создали бы меню и привязали его к иконке в трее, делая это в  процессе рендеринга . Добавьте следующее в  index.js :

var remote = require('remote');
var Tray = remote.require('tray');
var Menu = remote.require('menu');
var path = require('path');

var trayIcon = null;

if (process.platform === 'darwin') {
    trayIcon = new Tray(path.join(__dirname, 'img/tray-iconTemplate.png'));
}
else {
    trayIcon = new Tray(path.join(__dirname, 'img/tray-icon-alt.png'));
}

var trayMenuTemplate = [
    {
        label: 'Sound machine',
        enabled: false
    },
    {
        label: 'Settings',
        click: function () {
            ipc.send('open-settings-window');
        }
    },
    {
        label: 'Quit',
        click: function () {
            ipc.send('close-main-window');
        }
    }
];
var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
trayIcon.setContextMenu(trayMenu);

Родные модули GUI ( меню  и  трей ) требовались удаленно, и поэтому их можно использовать здесь.

Значок в трее определяется через его значок. OS X поддерживает шаблоны изображений (условно, изображение считается изображением шаблона, если его имя заканчивается на «Шаблон»), что позволяет легко работать с темными и светлыми темами. Другие ОС получают обычный значок.

Существует несколько способов создания меню в Electron. Таким образом создается шаблон меню (простой массив с элементами меню) и создается меню из этого шаблона. В конце к новому меню прикрепляется иконка в трее.

Упаковка вашей заявки

Follow along with the tag 07-ready-for-packaging:
git checkout 07-ready-for-packaging

Какая польза от приложения, которое вы не можете позволить людям загружать и использовать?упаковка

Упаковать ваше приложение для всех платформ легко,  используя  электронный упаковщик . В двух словах, электронный упаковщик абстрагирует всю работу, связанную с упаковкой вашего приложения с Electron, и создает все платформы, для которых вы собираетесь публиковать.

Его можно использовать как приложение CLI или как часть процесса сборки. Создание более сложного сценария сборки не входит в сферу этой статьи, но мы будем использовать возможности  сценариев npm для упрощения упаковки. Использование electronic-packager тривиально, общая форма при упаковке приложения:

electron-packager <location of project> <name of project> <platform> <architecture> <electron version> <optional options>

Где:

  • Расположение проекта указывает на папку, где находится ваш проект,
  • Название проекта определяет название вашего проекта,
  • Платформа решает, для каких платформ строить ( все  для сборки под Windows, Mac и Linux),
  • Архитектура решает, какую архитектуру строить (x86 или x64,  все  для обоих) и
  • Версия Electron позволяет выбрать, какую версию Electron использовать.

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

Я упаковываю звуковую машину обычно так (на Mac):

electron-packager ~/Projects/sound-machine SoundMachine --all --version=0.30.2 --out=~/Desktop --overwrite --icon=~/Projects/sound-machine/app/img/app-icon.icns

Новые опции, включенные в команду, не требуют пояснений. Чтобы получить красивую иконку, сначала нужно преобразовать ее в .icns (для Mac) и / или .ico (для Windows). Просто найдите инструмент для преобразования вашего PNG-файла в эти форматы, подобные  этому  (обязательно скачайте файл с  расширением .icns, а не  .hqx ). Если упаковка для Windows выполняется не из ОС Windows, вам понадобится вино на вашем пути (пользователи Mac могут использовать brew, а пользователи Linux могут использовать apt-get).

Нет смысла запускать эту большую команду каждый раз. Мы можем добавить еще один скрипт в наш package.json . Прежде всего, установите электронный упаковщик как зависимость для разработки:

npm install --save-dev electron-packager

Теперь мы можем добавить новый скрипт в наш   файл package.json :

"scripts": {
    "start": "electron .",
    "package": "electron-packager ./ SoundMachine --all --out ~/Desktop/SoundMachine --version 0.30.2 --overwrite --icon=./app/img/app-icon.icns"
}

И затем запустите следующее в CLI:

npm run-script package

Команда package запускает электронный упаковщик, просматривает текущий каталог и выполняет сборку на рабочий стол. Сценарий должен быть изменен, если вы используете Windows, но это тривиально.

Звуковая машина в своем нынешнем состоянии весит колоссальные 100 МБ. Не волнуйтесь, после того, как вы его заархивируете (zip или тип архива по вашему выбору), он потеряет более половины своего размера.

Если вы действительно хотите поехать в город, посмотрите на  электронный строитель,  который берет пакеты, созданные  электронным упаковщиком,  и создает автоматические установщики.

Дополнительные функции для добавления

Теперь, когда приложение упаковано и готово к работе, вы можете приступить к разработке собственных функций.

Вот несколько идей:

  • Экран справки с информацией о приложении, его ярлыках и авторе,
  • Добавление значка и пункта меню, чтобы открыть информационный экран,
  • Создайте хороший скрипт для быстрой сборки и распространения,
  • Добавьте уведомления, используя  node-notifier,  чтобы пользователи знали, какой звук они воспроизводят,
  • В  большей степени используйте  lodash для более чистой базы кода (например, для перебора массивов),
  • Сократите все ваши CSS и JavaScript с помощью инструмента сборки перед упаковкой,
  • Объедините вышеупомянутый узел-уведомитель с серверным вызовом, чтобы проверить наличие новых версий вашего приложения и уведомить пользователей …

Для хорошей задачи — попробуйте извлечь логику Windows вашего браузера Sound machine и использовать что-то вроде browserify для создания веб-страницы с той же звуковой машиной, которую вы только что создали. Одна кодовая база — два продукта (настольное приложение и веб-приложение). Острота!

Погружение глубже в электрон

Мы только поцарапали поверхность того, что Электрон приносит на стол. Это довольно легко сделать, например, наблюдать за событиями питания на хост-машине или получать различную информацию на экране (например, положение курсора).

Для всех этих встроенных утилит (и вообще при разработке приложений с Electron), ознакомьтесь с документацией Electron API .

Эти документы Electron API являются частью папки документов в репозитории Electron GitHub, и эту папку стоит проверить.

Sindre Sorhus предлагает  великолепный список ресурсов Electron,  в котором вы можете найти действительно интересные проекты и информацию, например, отличный обзор типичной архитектуры приложений Electron, которая может служить обновлением кода, который мы разрабатывали до сих пор.

В конце концов, Electron основан на io.js (который будет объединен с Node.js), и большинство модулей Node.js совместимы и могут использоваться для расширения вашего приложения. Просто просмотрите npmjs.com  и получите то, что вам нужно.

В том, что все?

Отнюдь не.

Теперь пришло время создать приложение большего размера. В этом руководстве я в основном пропустил использование дополнительных библиотек или инструментов сборки, чтобы сконцентрироваться на важных вопросах, но вы можете легко написать свое приложение на ES6 или Typescript, использовать Angular или React и упростить сборку с помощью Gulp или Grunt.

С вашим любимым языком, фреймворком и инструментом для сборки, почему бы не создать настольное приложение синхронизации Flickr с помощью API-интерфейса Flickr и node-flickrapi или клиента GMail с помощью официальной клиентской библиотеки Google Node.js?

Выберите идею, которая будет мотивировать вас, создайте репозиторий и просто сделайте это.