diff --git a/deploy/deployment.json b/deploy/deployment.json
index af1d09b8..0894c9d3 100644
--- a/deploy/deployment.json
+++ b/deploy/deployment.json
@@ -17,6 +17,11 @@
"${ROOT}/src/ui/i18n | *.qm |"
]
},
+ "${TARGET}/driver": {
+ "fort": [
+ "${ROOT}/src/driver/scripts | *.bat *.lnk |"
+ ]
+ },
"${TARGET}/imports": {
"common": [
"${QTPATH}/qml/QtQuick.2 | *.dll *.qmltypes qmldir | *d.dll"
diff --git a/deploy/setup-deployment.bat b/deploy/setup-deployment.bat
index 96495e4d..c2444e1e 100644
--- a/deploy/setup-deployment.bat
+++ b/deploy/setup-deployment.bat
@@ -6,7 +6,7 @@
md %TARGET_PATH%
cd %TARGET_PATH%
del /Q /F qt*.* FortFirewall.exe
-rd /Q /S i18n imports plugins scripts
+rd /Q /S driver\scripts i18n imports plugins scripts
cd ..
powershell.exe -executionpolicy remotesigned -file setup-deployment.ps1 %TARGET_PATH% %*
diff --git a/src/driver/scripts/execute-cmd.bat b/src/driver/scripts/execute-cmd.bat
new file mode 100644
index 00000000..8e7e3441
--- /dev/null
+++ b/src/driver/scripts/execute-cmd.bat
@@ -0,0 +1,11 @@
+@rem Execute the command
+
+@cd %~dp0
+@echo off
+
+set "SystemPath=%SystemRoot%\System32"
+if defined PROGRAMFILES(X86) (
+ if exist %SystemRoot%\Sysnative\* set "SystemPath=%SystemRoot%\Sysnative"
+)
+
+%SystemPath%\cmd.exe /C start /W %1
diff --git a/src/driver/scripts/install.bat b/src/driver/scripts/install.bat
new file mode 100644
index 00000000..df46ad04
--- /dev/null
+++ b/src/driver/scripts/install.bat
@@ -0,0 +1,53 @@
+@rem Install driver
+
+@set DISPNAME=Fort Firewall
+
+@set ARCH=32
+@if defined PROGRAMFILES(X86) @set ARCH=64
+
+@set BASENAME=fortfw
+@set FILENAME=%BASENAME%%ARCH%.sys
+@set SRCPATH=%~dp0..\%FILENAME%
+@set DSTPATH=%SystemRoot%\System32\drivers\%BASENAME%.sys
+
+
+@rem Copy driver to system storage
+@if exist "%DSTPATH%" (
+ @echo Error: Driver already installed. Uninstall it first
+ @set RCODE=1
+ @goto EXIT
+)
+
+copy "%SRCPATH%" "%DSTPATH%"
+@if ERRORLEVEL 1 (
+ @echo Error: Cannot copy driver to system
+ @set RCODE=%ERRORLEVEL%
+ @goto EXIT
+)
+
+
+@rem Create service
+sc create %BASENAME% binPath= "%DSTPATH%" type= kernel start= auto depend= BFE DisplayName= "%DISPNAME%"
+@if ERRORLEVEL 1 (
+ @echo Error: Cannot create a service
+ @set RCODE=%ERRORLEVEL%
+ @goto EXIT
+)
+
+sc start %BASENAME%
+@if ERRORLEVEL 1 (
+ @echo Error: Cannot start the service
+ @set RCODE=%ERRORLEVEL%
+ @goto EXIT
+)
+
+
+@set RCODE=0
+@goto EXIT
+
+:EXIT
+@echo End execution... Error Code = %RCODE%
+@if %RCODE% neq 0 (
+ @pause
+)
+@exit /b %RCODE%
diff --git a/src/driver/scripts/reinstall.bat b/src/driver/scripts/reinstall.bat
new file mode 100644
index 00000000..b98ae5cc
--- /dev/null
+++ b/src/driver/scripts/reinstall.bat
@@ -0,0 +1,8 @@
+@rem Re-install driver
+
+@cd %~dp0
+@echo off
+
+%COMSPEC% /C uninstall.bat
+
+install.bat
diff --git a/src/driver/scripts/reinstall.lnk b/src/driver/scripts/reinstall.lnk
new file mode 100644
index 00000000..0980f99a
Binary files /dev/null and b/src/driver/scripts/reinstall.lnk differ
diff --git a/src/driver/scripts/uninstall.bat b/src/driver/scripts/uninstall.bat
new file mode 100644
index 00000000..62579516
--- /dev/null
+++ b/src/driver/scripts/uninstall.bat
@@ -0,0 +1,33 @@
+@rem Uninstall driver
+
+@set DISPNAME=Fort Firewall
+
+@set BASENAME=fortfw
+@set DSTPATH=%SystemRoot%\System32\drivers\%BASENAME%.sys
+
+
+@rem Close the "Services" list window
+taskkill /F /IM mmc.exe
+
+@rem Stop the service
+net stop %BASENAME%
+
+@rem Remove the service
+sc delete %BASENAME%
+@if ERRORLEVEL 1 (
+ @echo Error: Cannot delete the service
+ @set RCODE=%ERRORLEVEL%
+ @rem @goto EXIT
+)
+
+
+@rem Remove driver from system storage
+Del "%DSTPATH%"
+
+
+@set RCODE=0
+@goto EXIT
+
+:EXIT
+@echo End execution... Error Code = %RCODE%
+@exit /b %RCODE%
diff --git a/src/driver/scripts/uninstall.lnk b/src/driver/scripts/uninstall.lnk
new file mode 100644
index 00000000..7023d176
Binary files /dev/null and b/src/driver/scripts/uninstall.lnk differ
diff --git a/src/ui/driver/drivermanager.cpp b/src/ui/driver/drivermanager.cpp
index d183b785..51cf02b5 100644
--- a/src/ui/driver/drivermanager.cpp
+++ b/src/ui/driver/drivermanager.cpp
@@ -50,17 +50,23 @@ bool DriverManager::isDeviceOpened() const
bool DriverManager::openDevice()
{
- if (!m_device->open(FortCommon::deviceName())) {
+ const bool res = m_device->open(FortCommon::deviceName());
+ if (!res) {
setErrorMessage(OsUtil::lastErrorMessage());
- return false;
}
- return true;
+ emit isDeviceOpenedChanged();
+
+ return res;
}
bool DriverManager::closeDevice()
{
- return m_device->close();
+ const bool res = m_device->close();
+
+ emit isDeviceOpenedChanged();
+
+ return res;
}
bool DriverManager::validate()
@@ -121,12 +127,22 @@ bool DriverManager::writeData(quint32 code, QByteArray &buf, int size)
}
void DriverManager::reinstallDriver()
+{
+ executeCommand("reinstall.lnk");
+}
+
+void DriverManager::uninstallDriver()
+{
+ executeCommand("uninstall.lnk");
+}
+
+void DriverManager::executeCommand(const QString &fileName)
{
const QString binPath = FileUtil::toNativeSeparators(
FileUtil::appBinLocation());
const QString cmdPath = qEnvironmentVariable("COMSPEC");
- const QString scriptPath = binPath + "\\driver\\scripts\\reinstall-lnk.bat";
+ const QString scriptPath = binPath + "\\driver\\scripts\\execute-cmd.bat";
- QProcess::execute(cmdPath, QStringList() << "/C" << scriptPath);
+ QProcess::execute(cmdPath, QStringList() << "/C" << scriptPath << fileName);
}
diff --git a/src/ui/driver/drivermanager.h b/src/ui/driver/drivermanager.h
index ee759f21..17088d56 100644
--- a/src/ui/driver/drivermanager.h
+++ b/src/ui/driver/drivermanager.h
@@ -11,6 +11,7 @@ class DriverManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
+ Q_PROPERTY(bool isDeviceOpened READ isDeviceOpened NOTIFY isDeviceOpenedChanged)
public:
explicit DriverManager(QObject *parent = nullptr);
@@ -23,9 +24,11 @@ public:
bool isDeviceOpened() const;
static void reinstallDriver();
+ static void uninstallDriver();
signals:
void errorMessageChanged();
+ void isDeviceOpenedChanged();
public slots:
bool openDevice();
@@ -44,6 +47,8 @@ private:
bool writeData(quint32 code, QByteArray &buf, int size);
+ static void executeCommand(const QString &fileName);
+
private:
Device *m_device;
DriverWorker *m_driverWorker;
diff --git a/src/ui/fort_images.qrc b/src/ui/fort_images.qrc
index 1b83e636..5e261092 100644
--- a/src/ui/fort_images.qrc
+++ b/src/ui/fort_images.qrc
@@ -21,6 +21,9 @@
images/link.png
images/page_copy.png
images/page_paste.png
+ images/plugin.png
+ images/plugin_disabled.png
+ images/plugin_error.png
images/resultset_next.png
images/resultset_previous.png
images/run.png
diff --git a/src/ui/fort_qml.qrc b/src/ui/fort_qml.qrc
index a2a6f68c..b9fd7b6f 100644
--- a/src/ui/fort_qml.qrc
+++ b/src/ui/fort_qml.qrc
@@ -2,6 +2,7 @@
qml/controls/ButtonMenu.qml
qml/controls/ButtonPopup.qml
+ qml/controls/DelayButtonControl.qml
qml/controls/HSeparator.qml
qml/controls/LabelColorRow.qml
qml/controls/LabelSpinRow.qml
diff --git a/src/ui/fortmanager.cpp b/src/ui/fortmanager.cpp
index 09b208e2..87e9909c 100644
--- a/src/ui/fortmanager.cpp
+++ b/src/ui/fortmanager.cpp
@@ -62,6 +62,7 @@ FortManager::FortManager(FortSettings *fortSettings,
{
setupLogger();
setupDatabaseManager();
+
setupLogManager();
setupDriver();
@@ -80,6 +81,7 @@ FortManager::~FortManager()
removeHotKeys();
closeDriver();
+ closeLogManager();
}
void FortManager::registerQmlTypes()
@@ -119,28 +121,35 @@ void FortManager::registerQmlTypes()
qmlRegisterType("com.fortfirewall", 1, 0, "StringUtil");
}
+void FortManager::installDriver()
+{
+ closeDriver();
+
+ m_driverManager->reinstallDriver();
+
+ if (setupDriver()) {
+ updateDriverConf(m_firewallConf);
+ }
+}
+
+void FortManager::removeDriver()
+{
+ closeDriver();
+
+ m_driverManager->uninstallDriver();
+}
+
bool FortManager::setupDriver()
{
bool opened = m_driverManager->openDevice();
- // Try to (re)install the driver in portable mode
- if (m_fortSettings->isPortable()
- && !(opened && m_driverManager->validate())) {
- if (opened) {
- m_driverManager->closeDevice();
- }
+ if (!m_driverManager->validate()) {
+ m_driverManager->closeDevice();
- m_driverManager->reinstallDriver();
-
- opened = m_driverManager->openDevice();
+ opened = false;
}
- if (!opened) {
- showErrorBox("Setup Driver: " + m_driverManager->errorMessage());
- return false;
- }
-
- return true;
+ return opened;
}
void FortManager::closeDriver()
@@ -148,6 +157,15 @@ void FortManager::closeDriver()
updateLogManager(false);
m_driverManager->closeDevice();
+}
+
+void FortManager::setupLogManager()
+{
+ m_logManager->initialize();
+}
+
+void FortManager::closeLogManager()
+{
m_logManager->close();
}
@@ -169,11 +187,6 @@ void FortManager::setupLogger()
updateLogger();
}
-void FortManager::setupLogManager()
-{
- m_logManager->initialize();
-}
-
void FortManager::setupTranslationManager()
{
TranslationManager::instance()->switchLanguageByName(
@@ -201,6 +214,7 @@ bool FortManager::setupEngine()
QQmlContext *context = m_engine->rootContext();
context->setContextProperty("fortManager", this);
+ context->setContextProperty("driverManager", m_driverManager);
context->setContextProperty("translationManager", TranslationManager::instance());
m_engine->load(QUrl("qrc:/qml/main.qml"));
@@ -486,8 +500,6 @@ bool FortManager::updateDriverConf(FirewallConf *conf, bool onlyFlags)
if (res) {
updateDatabaseManager(conf);
- } else {
- showErrorBox("Update Driver Conf: " + m_driverManager->errorMessage());
}
updateLogManager(true);
diff --git a/src/ui/fortmanager.h b/src/ui/fortmanager.h
index 00b160e1..adf95b2a 100644
--- a/src/ui/fortmanager.h
+++ b/src/ui/fortmanager.h
@@ -47,6 +47,9 @@ signals:
void firewallConfToEditChanged();
public slots:
+ void installDriver();
+ void removeDriver();
+
void launch();
void showTrayIcon();
@@ -90,10 +93,12 @@ private:
bool setupDriver();
void closeDriver();
+ void setupLogManager();
+ void closeLogManager();
+
void setupDatabaseManager();
void setupLogger();
- void setupLogManager();
void setupTranslationManager();
diff --git a/src/ui/i18n/i18n_ru.qm b/src/ui/i18n/i18n_ru.qm
index 18d2c8be..cadb9c5e 100644
Binary files a/src/ui/i18n/i18n_ru.qm and b/src/ui/i18n/i18n_ru.qm differ
diff --git a/src/ui/i18n/i18n_ru.ts b/src/ui/i18n/i18n_ru.ts
index 9dc6fb3b..0971555e 100644
--- a/src/ui/i18n/i18n_ru.ts
+++ b/src/ui/i18n/i18n_ru.ts
@@ -50,42 +50,42 @@
FortManager
-
+
Ввод пароля
-
+
Наберите пароль пожалуйста
-
+
Опции
-
+
График трафика
-
+
Фильтр включен
-
+
Остановить трафик
-
+
Остановить Интернет трафик
-
+
Выйти
@@ -371,7 +371,7 @@
Статистика
-
+
Пароль:
@@ -396,55 +396,72 @@
Выйти
-
+
Запускать вместе с Windows
-
+
Остановить трафик, когда Fort Firewall не запущен
-
+
Фильтр включен
-
+
Фильтр локальных адресов
-
+
Остановить трафик
-
+
Остановить Интернет трафик
-
+
Горячие клавиши
-
+
+
Установлен
-
+
+
Не установлен
-
+
Язык:
+
+
+
+ Драйвер:
+
+
+
+
+ Установить
+
+
+
+
+ Удалить
+
diff --git a/src/ui/images/plugin.png b/src/ui/images/plugin.png
new file mode 100644
index 00000000..6187b15a
Binary files /dev/null and b/src/ui/images/plugin.png differ
diff --git a/src/ui/images/plugin_disabled.png b/src/ui/images/plugin_disabled.png
new file mode 100644
index 00000000..f4f6be59
Binary files /dev/null and b/src/ui/images/plugin_disabled.png differ
diff --git a/src/ui/images/plugin_error.png b/src/ui/images/plugin_error.png
new file mode 100644
index 00000000..cff65d7f
Binary files /dev/null and b/src/ui/images/plugin_error.png differ
diff --git a/src/ui/main.cpp b/src/ui/main.cpp
index 39be0d67..71534d48 100644
--- a/src/ui/main.cpp
+++ b/src/ui/main.cpp
@@ -11,8 +11,7 @@
#include "util/osutil.h"
#define FORT_ERROR_INSTANCE 1
-#define FORT_ERROR_DEVICE 2
-#define FORT_ERROR_CONTROL 3
+#define FORT_ERROR_CONTROL 2
int main(int argc, char *argv[])
{
@@ -52,14 +51,6 @@ int main(int argc, char *argv[])
}
FortManager fortManager(&fortSettings);
-
- // Error: Cannot open the driver device
- if (!fortManager.driverManager()->isDeviceOpened()) {
- QMessageBox::critical(nullptr, QString(),
- "Cannot open the driver device!");
- return FORT_ERROR_DEVICE;
- }
-
fortManager.launch();
// Process control requests from clients
diff --git a/src/ui/qml/controls/DelayButtonControl.qml b/src/ui/qml/controls/DelayButtonControl.qml
new file mode 100644
index 00000000..9a6c34da
--- /dev/null
+++ b/src/ui/qml/controls/DelayButtonControl.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+DelayButton {
+
+ signal delayClicked()
+
+ property bool isActivated: false
+
+ onPressed: {
+ isActivated = false;
+ }
+ onActivated: {
+ isActivated = true;
+ }
+ onReleased: {
+ if (isActivated) {
+ delayClicked();
+ checked = false;
+ }
+ }
+}
diff --git a/src/ui/qml/pages/OptionsPage.qml b/src/ui/qml/pages/OptionsPage.qml
index f7571f9e..9c3d695b 100644
--- a/src/ui/qml/pages/OptionsPage.qml
+++ b/src/ui/qml/pages/OptionsPage.qml
@@ -36,144 +36,197 @@ BasePage {
Frame {
anchors.fill: parent
- ColumnLayout {
+ RowLayout {
anchors.fill: parent
- spacing: 10
- CheckBox {
- id: cbStart
- text: translationManager.trTrigger
- && qsTranslate("qml", "Start with Windows")
- checked: fortSettings.startWithWindows
- onToggled: setIniEdited()
- }
-
- CheckBox {
- text: translationManager.trTrigger
- && qsTranslate("qml", "Stop traffic when Fort Firewall is not running")
- checked: firewallConf.provBoot
- onToggled: {
- firewallConf.provBoot = checked;
-
- setConfFlagsEdited();
- }
- }
-
- Row {
- spacing: 20
+ ColumnLayout {
+ Layout.alignment: Qt.AlignTop
+ spacing: 10
CheckBox {
- id: cbFilterEnabled
- width: Math.max(implicitWidth, cbStopTraffic.implicitWidth)
+ id: cbStart
text: translationManager.trTrigger
- && qsTranslate("qml", "Filter Enabled")
- checked: firewallConf.filterEnabled
+ && qsTranslate("qml", "Start with Windows")
+ checked: fortSettings.startWithWindows
+ onToggled: setIniEdited()
+ }
+
+ CheckBox {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Stop traffic when Fort Firewall is not running")
+ checked: firewallConf.provBoot
onToggled: {
- firewallConf.filterEnabled = checked;
+ firewallConf.provBoot = checked;
setConfFlagsEdited();
}
}
- CheckBox {
- text: translationManager.trTrigger
- && qsTranslate("qml", "Filter Local Addresses")
- checked: firewallConf.filterLocals
- onToggled: {
- firewallConf.filterLocals = checked;
+ Row {
+ spacing: 20
- setConfFlagsEdited();
+ CheckBox {
+ id: cbFilterEnabled
+ width: Math.max(implicitWidth, cbStopTraffic.implicitWidth)
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Filter Enabled")
+ checked: firewallConf.filterEnabled
+ onToggled: {
+ firewallConf.filterEnabled = checked;
+
+ setConfFlagsEdited();
+ }
+ }
+
+ CheckBox {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Filter Local Addresses")
+ checked: firewallConf.filterLocals
+ onToggled: {
+ firewallConf.filterLocals = checked;
+
+ setConfFlagsEdited();
+ }
}
}
- }
- Row {
- spacing: 20
+ Row {
+ spacing: 20
- CheckBox {
- id: cbStopTraffic
- width: cbFilterEnabled.width
- text: translationManager.trTrigger
- && qsTranslate("qml", "Stop Traffic")
- checked: firewallConf.stopTraffic
- onToggled: {
- firewallConf.stopTraffic = checked;
+ CheckBox {
+ id: cbStopTraffic
+ width: cbFilterEnabled.width
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Stop Traffic")
+ checked: firewallConf.stopTraffic
+ onToggled: {
+ firewallConf.stopTraffic = checked;
- setConfFlagsEdited();
+ setConfFlagsEdited();
+ }
+ }
+
+ CheckBox {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Stop Internet Traffic")
+ checked: firewallConf.stopInetTraffic
+ onToggled: {
+ firewallConf.stopInetTraffic = checked;
+
+ setConfFlagsEdited();
+ }
}
}
CheckBox {
+ id: cbHotKeys
text: translationManager.trTrigger
- && qsTranslate("qml", "Stop Internet Traffic")
- checked: firewallConf.stopInetTraffic
- onToggled: {
- firewallConf.stopInetTraffic = checked;
+ && qsTranslate("qml", "Hot Keys")
+ checked: fortSettings.hotKeyEnabled
+ onToggled: setIniEdited()
+ }
- setConfFlagsEdited();
+ Row {
+ spacing: 4
+
+ CheckBox {
+ id: cbPassword
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Password:")
+ checked: firewallConf.hasPassword
+ onToggled: {
+ if (!checked) {
+ firewallConf.passwordHash =
+ editPassword.text = "";
+ } else {
+ editPassword.forceActiveFocus();
+ }
+
+ setConfEdited();
+ }
+ }
+ TextFieldFrame {
+ id: editPassword
+ width: 180
+ echoMode: TextInput.Password
+ passwordMaskDelay: 300
+ readOnly: firewallConf.hasPassword || !cbPassword.checked
+ placeholderText: translationManager.trTrigger
+ && (firewallConf.hasPassword
+ ? qsTranslate("qml", "Installed")
+ : qsTranslate("qml", "Not Installed"))
+ }
+ }
+
+ Row {
+ spacing: 4
+
+ Label {
+ anchors.verticalCenter: parent.verticalCenter
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Language:")
+ }
+ ComboBox {
+ width: Math.max(implicitWidth, 180)
+ flat: true
+ currentIndex: translationManager.language
+ model: translationManager.naturalLabels
+ onActivated: fortManager.setLanguage(index)
}
}
}
- CheckBox {
- id: cbHotKeys
- text: translationManager.trTrigger
- && qsTranslate("qml", "Hot Keys")
- checked: fortSettings.hotKeyEnabled
- onToggled: setIniEdited()
- }
+ ColumnLayout {
+ Layout.alignment: Qt.AlignTop
+ spacing: 10
- Row {
- spacing: 4
+ Frame {
+ Layout.alignment: Qt.AlignHCenter
- CheckBox {
- id: cbPassword
- text: translationManager.trTrigger
- && qsTranslate("qml", "Password:")
- checked: firewallConf.hasPassword
- onToggled: {
- if (!checked) {
- firewallConf.passwordHash =
- editPassword.text = "";
- } else {
- editPassword.forceActiveFocus();
+ ColumnLayout {
+ spacing: 10
+
+ Row {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 5
+ Image {
+ source: driverManager.isDeviceOpened
+ ? (!driverManager.errorMessage
+ ? "qrc:/images/plugin.png"
+ : "qrc:/images/plugin_error.png")
+ : "qrc:/images/plugin_disabled.png"
+ }
+ Label {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Driver:")
+ }
+ Label {
+ width: Math.min(implicitWidth, 300)
+ wrapMode: Text.Wrap
+ text: translationManager.trTrigger
+ && (driverManager.isDeviceOpened
+ ? (driverManager.errorMessage
+ || qsTranslate("qml", "Installed"))
+ : qsTranslate("qml", "Not Installed"))
+ }
}
- setConfEdited();
+ Row {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 10
+ DelayButtonControl {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Install")
+ onDelayClicked: fortManager.installDriver()
+ }
+ DelayButtonControl {
+ text: translationManager.trTrigger
+ && qsTranslate("qml", "Remove")
+ onDelayClicked: fortManager.removeDriver()
+ }
+ }
}
}
- TextFieldFrame {
- id: editPassword
- width: 180
- echoMode: TextInput.Password
- passwordMaskDelay: 300
- readOnly: firewallConf.hasPassword || !cbPassword.checked
- placeholderText: translationManager.trTrigger
- && (firewallConf.hasPassword
- ? qsTranslate("qml", "Installed")
- : qsTranslate("qml", "Not Installed"))
- }
- }
-
- Row {
- spacing: 4
-
- Label {
- anchors.verticalCenter: parent.verticalCenter
- text: translationManager.trTrigger
- && qsTranslate("qml", "Language:")
- }
- ComboBox {
- width: Math.max(implicitWidth, 180)
- flat: true
- currentIndex: translationManager.language
- model: translationManager.naturalLabels
- onActivated: fortManager.setLanguage(index)
- }
- }
-
- Item {
- Layout.fillHeight: true
}
}
}