NodeGUI — фреймворк, который позволяет писать нативные кроссплатформенные десктопные приложения. Под капотом он использует Qt для отрисовки интерфейса.

Есть возможность подключить React или Vue.

Также существует имплементация с Angular от irustm.

Эта статья будет опираться именно на версию с React.

Запуск и настройка


Сначала необходимо установить cmake.
Склонируем готовый шаблон от разработчиков и запустим его.

npm install
npm run dev
npm start

Чтобы приложение пересобиралось каждый раз, как в код вносятся изменения, к компоненту app подключается hot reload.

if (module.hot) {
 module.hot.accept(['./app'], function () {
   Renderer.forceUpdate();
 });
}

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

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

Есть возможность подключить скромные по своим умениям dev tools:

npm install --save-dev @nodegui/devtools

или

npm install -g react-devtools

Запускаем:

react-devtools

И немного меняем index.js

Renderer.render(<App />, {
 onInit: (reconciler) => {
   if (process.env.NODE_ENV === 'development') {
     require('@nodegui/devtools').connectReactDevtools(reconciler);
   }
 },
});

Самым первым элементом в иерархии интерфейса является Window, он представляет собой отдельное окно приложения, в котором уже будет отрисовываться все, что нам надо.

render() {
 return (
   <Window
     windowIcon={winIcon}
     windowTitle="Hello "
     minSize={minSize}
     styleSheet={styleSheet}
   >
     <View style={containerStyle}>
       <Text id="welcome-text">Welcome to NodeGui </Text>
       <Text id="step-1">1. Play around</Text>
       <StepOne />
       <Text id="step-2">2. Debug</Text>
       <StepTwo />
     </View>
   </Window>
 );
}

Компонент View представляет из себя простой контейнер, аналог div в html.

Layout


В NodeGUI есть несколько способов построения интерфейса: как строгим указанием размера каждого компонента, так и адаптивными GridView, BoxView и даже с помощью flex’ов.

BoxView отличается от View тем, что позволяет задать направление расположения дочерних элементов.

<BoxView direction={Direction.LeftToRight}>
 <Button text="Add" on={addHandler} />
 <Button text="Remove" on={removeHandler} />
</BoxView>

GridView тоже является компонентом, в который необходимо передавать н-ное количество GridRow для разбиения контейнера на строки, а в GridRow, соответственно, GridColumn с нужным контентом.

Намного интереснее дела обстоят с флексами, реализованными с помощью Yoga.

Стилизация и Flex


Для начала разберемся как стилизировать приложение. У каждого компонента есть такие пропсы как styleSheet и style.

В первый подается набор стилей для дочерних к контейнеру компонентов, в качестве селектора выступает id.

const styleSheet = `
 #welcome-text {
   font-size: 24px;
   padding-top: 20px;
   qproperty-alignment: 'AlignHCenter';
   font-family: 'sans-serif';
 }

 #step-1, #step-2 {
   font-size: 18px;
   padding-top: 10px;
   padding-horizontal: 20px;
 }
`;

Во втором же случае мы напрямую передаем стили в компонент.

<View style="flex: 1;" />

Но намного удобнее стилизировать с помощью библиотеки nodegui-stylesheet, т.к. она позволяет использовать объекты вместо строк при написании.

Воспользуемся функцией create, в которую отправим нужные нам стили.

import { create } from 'nodegui-stylesheet';

const style = create({
 wrapper: {
   flex: 1,
 },
 logo: {
   fontSize: 14,
 },
});

Дальше просто передаем подходящий стиль в свойство style.

<View style={style.wrapper}>
 <Text style={style.logo}>Hello</Text>
</View>

Отличие flex layout от остальных в том, что он реализуется с помощью стилей
Чтобы включить “flex режим” у компонента, нужно прописать ему атрибут flex: 1

Чем больше цифра, тем “приоритетнее” будет компонент и тем больше места он будет занимать.

Стили в nodegui напоминают css, но по сути им не являются, соответственно и атрибуты доступны не все (все доступные можно подглядеть в документации Qt). То же, к сожалению, касается и флексов, будьте осторожны.

События


Отслеживание событий у элементов тоже отличается от стандартного для React подхода.

Чтобы написать event handler в nodegui-react, надо воспользоваться хуком useEventHandler.

import { View, Button, useEventHandler } from '@nodegui/react-nodegui';
import React from 'react';
import { QPushButtonSignals } from '@nodegui/nodegui';

const TopBar = ({ onAdd }) => {
 const buttonHandler = useEventHandler<QPushButtonSignals>(
   {
     clicked: () => {
       onAdd();
     },
   },
   [onAdd],
 );

 return (
   <View>
     <Button on={buttonHandler} />
   </View>
 );
};

В данном случае мы обрабатываем событие именно кнопки, поэтому нужно указать тип QPushButtonSignals. Сам хук принимает объект, в котором интересующим нас событиям присваивается обработчик.

Теперь на каждое нажатие кнопки будет вызываться функция onAdd()

Заключение


Мы рассмотрели все необходимое для написания интерфейсов на NodeGUI. Напоследок скажу, что для серьезных проектов nodegui-react сейчас использовать не стоит, так даже в readme репозитория сказано, т.к. технология новая и сырая, и с ней возникает много проблем.