mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-22 02:01:08 +07:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 829d11bae6 | |||
| 54b5f4cbed | |||
| 82beb05741 | |||
| e0eb18d0f3 | |||
| 36dc2fd05d | |||
| 80418e8ae6 | |||
| 29772cc174 | |||
| 1ecbd2dc92 | |||
| d320faa4b7 | |||
| f515eb82be | |||
| 243b988209 | |||
| fd8dfef176 | |||
| 60f32f7b0d | |||
| 810756eda8 | |||
| 9e805635c3 | |||
| a6f52bfd90 | |||
| b0722cabd7 | |||
| af252a36ab | |||
| 1cefde84d6 | |||
| 36ed95aa33 | |||
| e6b9d1dcda | |||
| 9d4d493095 | |||
| 53a466d7d7 | |||
| af425eff42 | |||
| 9ffeedffae | |||
| bcdf6eec41 | |||
| a1c380127a | |||
| b8890db314 | |||
| 0b30c854c5 | |||
| c34bdf0983 | |||
| 521a78f1a3 | |||
| ca224b22fa | |||
| 9817ad6daf | |||
| f814b875d1 | |||
| b5856386ee | |||
| 41da88fee3 | |||
| 02ec0f72c7 | |||
| 28270ff269 | |||
| 11e54ed12e | |||
| 3315675c11 | |||
| 23700ed914 | |||
| f15c2daa4c | |||
| 0e0500b3f3 | |||
| 73e3cb197a | |||
| 7b4a94dd3d | |||
| 9e30039eaa | |||
| 59a39719fd | |||
| cc7e6c651b | |||
| fbd5b0a20b | |||
| 405bd70267 | |||
| 04ee62ea90 | |||
| ad119d5de5 | |||
| 1a08b24cb8 | |||
| 73f94e1277 | |||
| b0fb3491ca | |||
| 62770f4c04 | |||
| 2b0faca362 | |||
| 5533ce56d7 | |||
| a7e812f6b2 | |||
| a43f7a6926 | |||
| 47f917de0b | |||
| dbeb7edd7a | |||
| 6cede712f5 | |||
| d328739192 | |||
| d15c0bd962 | |||
| d53c794936 | |||
| e5dcb25a4a | |||
| f9002b4f43 | |||
| 0531508a75 | |||
| 174e85a20a | |||
| e9abb6f1e2 | |||
| 5be44f9596 | |||
| 90efaaff92 | |||
| 99b554e7c3 | |||
| ac0ce8a6f6 | |||
| 9f9da885b7 | |||
| f51fd2bf3e | |||
| c8378fd32d | |||
| d767214f10 |
@@ -9,7 +9,6 @@ deploy/build_32/*
|
||||
deploy/build_64/*
|
||||
winbuild*.bat
|
||||
.cache/
|
||||
.vscode/
|
||||
|
||||
|
||||
# Qt-es
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.9.0)
|
||||
set(AMNEZIAVPN_VERSION 4.8.9.2)
|
||||
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2090)
|
||||
set(APP_ANDROID_VERSION_CODE 2092)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
|
||||
+1
-1
Submodule client/3rd-prebuilt updated: 840b7b070e...522e8d33a9
@@ -10,6 +10,8 @@ import java.nio.channels.FileChannel
|
||||
import java.nio.channels.FileLock
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import org.amnezia.vpn.util.Log.Priority.D
|
||||
import org.amnezia.vpn.util.Log.Priority.E
|
||||
@@ -135,8 +137,8 @@ object Log {
|
||||
}
|
||||
|
||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||
val date = LocalDateTime.now().format(dateTimeFormat)
|
||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).format(dateTimeFormat)
|
||||
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||
"$tag: $msg\n"
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ set(LIBS ${LIBS}
|
||||
${FW_NETWORK_EXTENSION}
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QTranslator>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include "core/installedAppsImageProvider.h"
|
||||
@@ -101,9 +100,6 @@ void CoreController::initModels()
|
||||
|
||||
m_apiDevicesModel.reset(new ApiDevicesModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ApiDevicesModel", m_apiDevicesModel.get());
|
||||
|
||||
m_newsModel.reset(new NewsModel(m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("NewsModel", m_newsModel.get());
|
||||
}
|
||||
|
||||
void CoreController::initControllers()
|
||||
@@ -124,6 +120,9 @@ void CoreController::initControllers()
|
||||
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
|
||||
&ConnectionController::onCurrentContainerUpdated); // TODO remove this
|
||||
|
||||
connect(m_installController.get(), &InstallController::profileCleared,
|
||||
m_protocolsModel.get(), &ProtocolsModel::updateModel);
|
||||
|
||||
m_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
|
||||
|
||||
@@ -155,10 +154,6 @@ void CoreController::initControllers()
|
||||
|
||||
m_apiPremV1MigrationController.reset(new ApiPremV1MigrationController(m_serversModel, m_settings, this));
|
||||
m_engine->rootContext()->setContextProperty("ApiPremV1MigrationController", m_apiPremV1MigrationController.get());
|
||||
|
||||
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
||||
m_apiNewsController->fetchNews();
|
||||
}
|
||||
|
||||
void CoreController::initAndroidController()
|
||||
@@ -251,6 +246,9 @@ void CoreController::initNotificationHandler()
|
||||
&ConnectionController::closeConnection);
|
||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||
#endif
|
||||
|
||||
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
|
||||
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||
}
|
||||
|
||||
void CoreController::updateTranslator(const QLocale &locale)
|
||||
@@ -287,6 +285,7 @@ void CoreController::updateTranslator(const QLocale &locale)
|
||||
m_engine->retranslate();
|
||||
|
||||
emit translationsUpdated();
|
||||
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
|
||||
}
|
||||
|
||||
void CoreController::initErrorMessagesHandler()
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
|
||||
#include "ui/systemtray_notificationhandler.h"
|
||||
|
||||
#include "ui/controllers/api/apiConfigsController.h"
|
||||
#include "ui/controllers/api/apiSettingsController.h"
|
||||
#include "ui/controllers/api/apiPremV1MigrationController.h"
|
||||
#include "ui/controllers/api/apiNewsController.h"
|
||||
#include "ui/controllers/appSplitTunnelingController.h"
|
||||
#include "ui/controllers/allowedDnsController.h"
|
||||
#include "ui/controllers/connectionController.h"
|
||||
@@ -44,7 +45,6 @@
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
#include "ui/models/newsmodel.h"
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include "ui/notificationhandler.h"
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
|
||||
signals:
|
||||
void translationsUpdated();
|
||||
void websiteUrlChanged(const QString &newUrl);
|
||||
|
||||
private:
|
||||
void initModels();
|
||||
@@ -115,7 +116,6 @@ private:
|
||||
QScopedPointer<ApiSettingsController> m_apiSettingsController;
|
||||
QScopedPointer<ApiConfigsController> m_apiConfigsController;
|
||||
QScopedPointer<ApiPremV1MigrationController> m_apiPremV1MigrationController;
|
||||
QScopedPointer<ApiNewsController> m_apiNewsController;
|
||||
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
QSharedPointer<ContainersModel> m_defaultServerContainersModel;
|
||||
@@ -123,7 +123,6 @@ private:
|
||||
QSharedPointer<LanguageModel> m_languageModel;
|
||||
QSharedPointer<ProtocolsModel> m_protocolsModel;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
QSharedPointer<NewsModel> m_newsModel;
|
||||
QSharedPointer<AllowedDnsModel> m_allowedDnsModel;
|
||||
QSharedPointer<AppSplitTunnelingModel> m_appSplitTunnelingModel;
|
||||
QSharedPointer<ClientManagementModel> m_clientManagementModel;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4_34)">
|
||||
<path d="M55.5 12.3333H18.5C15.0942 12.3333 12.3333 15.0943 12.3333 18.5V55.5C12.3333 58.9058 15.0942 61.6667 18.5 61.6667H55.5C58.9057 61.6667 61.6666 58.9058 61.6666 55.5V18.5C61.6666 15.0943 58.9057 12.3333 55.5 12.3333Z" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.5833 24.6667H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.5833 37H52.4167" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21.5833 49.3333H40.0833" stroke="#CBCAC8" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="61.5" cy="12.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4_34">
|
||||
<rect width="74" height="74" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 982 B |
@@ -1,8 +0,0 @@
|
||||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#CBCAC8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Основа газеты -->
|
||||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||||
<!-- Линии текста -->
|
||||
<line x1="7" y1="8" x2="17" y2="8"/>
|
||||
<line x1="7" y1="12" x2="17" y2="12"/>
|
||||
<line x1="7" y1="16" x2="13" y2="16"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 410 B |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="17.5" cy="17.5" r="15" fill="#FBB36B" stroke="#1C1D21" stroke-width="5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 188 B |
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
|
||||
<key>LSMultipleInstancesProhibited</key>
|
||||
<true/>
|
||||
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -35,9 +35,6 @@
|
||||
<file>images/controls/mail.svg</file>
|
||||
<file>images/controls/map-pin.svg</file>
|
||||
<file>images/controls/more-vertical.svg</file>
|
||||
<file>images/controls/news.svg</file>
|
||||
<file>images/controls/news-unread.svg</file>
|
||||
<file>images/controls/unread-dot.svg</file>
|
||||
<file>images/controls/plus.svg</file>
|
||||
<file>images/controls/qr-code.svg</file>
|
||||
<file>images/controls/radio-button-inner-circle-pressed.png</file>
|
||||
@@ -52,7 +49,6 @@
|
||||
<file>images/controls/server.svg</file>
|
||||
<file>images/controls/settings-2.svg</file>
|
||||
<file>images/controls/settings.svg</file>
|
||||
<file>images/controls/settings-news.svg</file>
|
||||
<file>images/controls/share-2.svg</file>
|
||||
<file>images/controls/split-tunneling.svg</file>
|
||||
<file>images/controls/tag.svg</file>
|
||||
@@ -131,7 +127,7 @@
|
||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||
<file>ui/qml/Components/ServersListView.qml</file>
|
||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
|
||||
|
||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
@@ -216,8 +212,6 @@
|
||||
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsServersList.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsNewsNotifications.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsNewsDetail.qml</file>
|
||||
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
|
||||
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
|
||||
@@ -234,6 +228,7 @@
|
||||
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
|
||||
<file>ui/qml/Pages2/PageShare.qml</file>
|
||||
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
|
||||
<file>ui/qml/Pages2/PageShareConnection.qml</file>
|
||||
<file>ui/qml/Pages2/PageStart.qml</file>
|
||||
<file>ui/qml/Components/RenameServerDrawer.qml</file>
|
||||
<file>ui/qml/Controls2/ListViewType.qml</file>
|
||||
@@ -246,6 +241,7 @@
|
||||
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
|
||||
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
|
||||
<file>ui/qml/Components/AwgTextField.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/countriesFlags">
|
||||
<file>images/flagKit/ZW.svg</file>
|
||||
|
||||
+1
-11
@@ -14,7 +14,7 @@ namespace
|
||||
const char cloudFlareNs1[] = "1.1.1.1";
|
||||
const char cloudFlareNs2[] = "1.0.0.1";
|
||||
|
||||
constexpr char gatewayEndpoint[] = "http://192.168.0.222:80/";
|
||||
constexpr char gatewayEndpoint[] = "http://gw.amnezia.org:80/";
|
||||
}
|
||||
|
||||
Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_NAME, APPLICATION_NAME, this)
|
||||
@@ -578,13 +578,3 @@ void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||
{
|
||||
setValue("Conf/allowedDnsServers", servers);
|
||||
}
|
||||
|
||||
QStringList Settings::readNewsIds() const
|
||||
{
|
||||
return value("News/readIds").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setReadNewsIds(const QStringList &ids)
|
||||
{
|
||||
setValue("News/readIds", ids);
|
||||
}
|
||||
|
||||
@@ -236,9 +236,6 @@ public:
|
||||
QStringList allowedDnsServers() const;
|
||||
void setAllowedDnsServers(const QStringList &servers);
|
||||
|
||||
QStringList readNewsIds() const;
|
||||
void setReadNewsIds(const QStringList &ids);
|
||||
|
||||
signals:
|
||||
void saveLogsChanged(bool enabled);
|
||||
void screenshotsEnabledChanged(bool enabled);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "apiNewsController.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
ApiNewsController::ApiNewsController(const QSharedPointer<NewsModel> &newsModel,
|
||||
const std::shared_ptr<Settings> &settings,
|
||||
QObject *parent)
|
||||
: QObject(parent), m_newsModel(newsModel), m_settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void ApiNewsController::fetchNews()
|
||||
{
|
||||
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(),
|
||||
apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled());
|
||||
QByteArray responseBody;
|
||||
QJsonObject payload;
|
||||
payload.insert("locale", m_settings->getAppLanguage().name().split("_").first());
|
||||
|
||||
ErrorCode errorCode = gatewayController.post(QString("%1v1/news"), payload, responseBody);
|
||||
qDebug() << "fetchNews" << errorCode;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(responseBody);
|
||||
QJsonArray newsArray;
|
||||
if (doc.isArray()) {
|
||||
newsArray = doc.array();
|
||||
} else if (doc.isObject()) {
|
||||
QJsonObject obj = doc.object();
|
||||
if (obj.value("news").isArray()) {
|
||||
newsArray = obj.value("news").toArray();
|
||||
}
|
||||
}
|
||||
|
||||
m_newsModel->updateModel(newsArray);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef APINEWSCONTROLLER_H
|
||||
#define APINEWSCONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <memory>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "settings.h"
|
||||
#include "ui/models/newsmodel.h"
|
||||
#include "core/controllers/gatewayController.h"
|
||||
#include "core/api/apiDefs.h"
|
||||
|
||||
class ApiNewsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ApiNewsController(const QSharedPointer<NewsModel> &newsModel,
|
||||
const std::shared_ptr<Settings> &settings,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void fetchNews();
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
|
||||
private:
|
||||
QSharedPointer<NewsModel> m_newsModel;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
};
|
||||
|
||||
#endif // APINEWSCONTROLLER_H
|
||||
@@ -4,6 +4,12 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickWindow>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("FocusController");
|
||||
}
|
||||
|
||||
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
|
||||
: QObject { parent },
|
||||
m_engine { engine },
|
||||
@@ -85,7 +91,7 @@ void FocusController::dropRootObject(QObject *object)
|
||||
dropListView();
|
||||
setFocusOnDefaultItem();
|
||||
} else {
|
||||
qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
|
||||
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +107,7 @@ void FocusController::reload(Direction direction)
|
||||
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
|
||||
|
||||
if (!rootObject) {
|
||||
qCritical() << "No ROOT OBJECT found!";
|
||||
logger.error() << "No ROOT OBJECT found!";
|
||||
resetRootObject();
|
||||
dropListView();
|
||||
return;
|
||||
@@ -113,7 +119,7 @@ void FocusController::reload(Direction direction)
|
||||
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
|
||||
|
||||
if (m_focusChain.empty()) {
|
||||
qWarning() << "Focus chain is empty!";
|
||||
logger.warning() << "Focus chain is empty!";
|
||||
resetRootObject();
|
||||
dropListView();
|
||||
return;
|
||||
@@ -131,7 +137,7 @@ void FocusController::nextItem(Direction direction)
|
||||
}
|
||||
|
||||
if (m_focusChain.empty()) {
|
||||
qWarning() << "There are no items to navigate";
|
||||
logger.warning() << "There are no items to navigate";
|
||||
setFocusOnDefaultItem();
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +155,7 @@ void FocusController::nextItem(Direction direction)
|
||||
const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex));
|
||||
|
||||
if (focusedItem == nullptr) {
|
||||
qWarning() << "Failed to get item to focus on. Setting focus on default";
|
||||
logger.warning() << "Failed to get item to focus on. Setting focus on default";
|
||||
setFocusOnDefaultItem();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -403,9 +403,19 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
QJsonObject config;
|
||||
Proto mainProto = ContainerProps::defaultProtocol(container);
|
||||
const auto &protocols = ContainerProps::protocolsForContainer(container);
|
||||
|
||||
for (const auto &protocol : protocols) {
|
||||
QJsonObject containerConfig;
|
||||
if (protocol == mainProto) {
|
||||
|
||||
// for Multiprotocols (OpenVPN over SS, OpenVPN over Cloak)
|
||||
bool shouldProcessProtocol = false;
|
||||
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
|
||||
shouldProcessProtocol = true;
|
||||
} else {
|
||||
shouldProcessProtocol = (protocol == mainProto);
|
||||
}
|
||||
|
||||
if (shouldProcessProtocol) {
|
||||
containerConfig.insert(config_key::port, port);
|
||||
containerConfig.insert(config_key::transport_proto, transportProto);
|
||||
|
||||
@@ -550,14 +560,97 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
|
||||
qDebug() << siteName;
|
||||
|
||||
containerConfig.insert(config_key::site, siteName);
|
||||
} else if (protocol == Proto::OpenVpn) {
|
||||
QString serverConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||
protocols::openvpn::serverConfigPath, errorCode);
|
||||
|
||||
QMap<QString, QString> serverConfigMap;
|
||||
auto serverConfigLines = serverConfig.split("\n");
|
||||
for (auto &line : serverConfigLines) {
|
||||
auto trimmedLine = line.trimmed();
|
||||
if (trimmedLine.startsWith("#") || trimmedLine.isEmpty()) {
|
||||
continue;
|
||||
} else {
|
||||
QStringList parts = trimmedLine.split(" ");
|
||||
if (parts.count() >= 2) {
|
||||
QString key = parts[0];
|
||||
QString value = parts.mid(1).join(" ");
|
||||
serverConfigMap.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString serverValue = serverConfigMap.value("server");
|
||||
|
||||
if (!serverValue.isEmpty()) {
|
||||
QStringList serverParts = serverValue.split(" ");
|
||||
if (serverParts.count() >= 1) {
|
||||
containerConfig[config_key::subnet_address] = serverParts[0];
|
||||
}
|
||||
}
|
||||
|
||||
bool ncpDisable = serverConfig.contains("ncp-disable");
|
||||
containerConfig[config_key::ncp_disable] = ncpDisable;
|
||||
|
||||
bool tlsAuth = serverConfig.contains("tls-auth");
|
||||
containerConfig[config_key::tls_auth] = tlsAuth;
|
||||
|
||||
bool blockOutsideDns = serverConfig.contains("block-outside-dns");
|
||||
|
||||
containerConfig[config_key::block_outside_dns] = blockOutsideDns;
|
||||
|
||||
QString cipher = serverConfigMap.value("cipher");
|
||||
if (!cipher.isEmpty()) {
|
||||
containerConfig[config_key::cipher] = cipher;
|
||||
}
|
||||
|
||||
QString hash = serverConfigMap.value("auth");
|
||||
if (!hash.isEmpty()) {
|
||||
containerConfig[config_key::hash] = hash;
|
||||
}
|
||||
} else if (protocol == Proto::Cloak) {
|
||||
QString cloakConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||
"/opt/amnezia/cloak/ck-config.json", errorCode);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(cloakConfig.toUtf8());
|
||||
|
||||
if (!doc.isNull() && doc.isObject()) {
|
||||
QJsonObject cloakConfigObj = doc.object();
|
||||
|
||||
QString site = cloakConfigObj.value("RedirAddr").toString();
|
||||
if (!site.isEmpty()) {
|
||||
containerConfig[config_key::site] = site;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Failed to parse main loop Cloak JSON config";
|
||||
}
|
||||
|
||||
} else if (protocol == Proto::ShadowSocks) {
|
||||
QString shadowsocksConfig = serverController->getTextFileFromContainer(container, credentials,
|
||||
"/opt/amnezia/shadowsocks/ss-config.json", errorCode);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(shadowsocksConfig.toUtf8());
|
||||
|
||||
if (!doc.isNull() && doc.isObject()) {
|
||||
QJsonObject ssConfigObj = doc.object();
|
||||
QString cipher = ssConfigObj.value("method").toString();
|
||||
if (!cipher.isEmpty()) {
|
||||
containerConfig[config_key::cipher] = cipher;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Failed to parse main loop Shadowsocks JSON config";
|
||||
}
|
||||
}
|
||||
|
||||
config.insert(config_key::container, ContainerProps::containerToString(container));
|
||||
}
|
||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||
if (shouldProcessProtocol) {
|
||||
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
|
||||
}
|
||||
}
|
||||
installedContainers.insert(container, config);
|
||||
}
|
||||
|
||||
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
|
||||
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
|
||||
if (torOrDnsRegMatch.hasMatch()) {
|
||||
@@ -721,6 +814,8 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
|
||||
m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController);
|
||||
|
||||
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container)));
|
||||
QJsonObject updatedConfig = m_settings->containerConfig(serverIndex, container);
|
||||
emit profileCleared(updatedConfig);
|
||||
}
|
||||
|
||||
QRegularExpression InstallController::ipAddressPortRegExp()
|
||||
|
||||
@@ -83,6 +83,8 @@ signals:
|
||||
|
||||
void noInstalledContainers();
|
||||
|
||||
void profileCleared(const QJsonObject &config);
|
||||
|
||||
private:
|
||||
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
|
||||
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
|
||||
|
||||
@@ -37,7 +37,7 @@ void ListViewFocusController::viewAtCurrentIndex() const
|
||||
}
|
||||
case Section::Delegate: {
|
||||
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
|
||||
Q_ARG(int, 2)); // PositionMode (0 = Visible)
|
||||
Q_ARG(int, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition)
|
||||
break;
|
||||
}
|
||||
case Section::Footer: {
|
||||
|
||||
@@ -26,8 +26,6 @@ namespace PageLoader
|
||||
PageSettingsConnection,
|
||||
PageSettingsDns,
|
||||
PageSettingsApplication,
|
||||
PageSettingsNewsNotifications,
|
||||
PageSettingsNewsDetail,
|
||||
PageSettingsBackup,
|
||||
PageSettingsAbout,
|
||||
PageSettingsLogging,
|
||||
@@ -40,6 +38,7 @@ namespace PageLoader
|
||||
PageSettingsApiInstructions,
|
||||
PageSettingsApiNativeConfigs,
|
||||
PageSettingsApiDevices,
|
||||
PageSettingsApiSubscriptionKey,
|
||||
PageSettingsKillSwitchExceptions,
|
||||
|
||||
PageServiceSftpSettings,
|
||||
@@ -73,6 +72,7 @@ namespace PageLoader
|
||||
PageProtocolAwgClientSettings,
|
||||
|
||||
PageShareFullAccess,
|
||||
PageShareConnection,
|
||||
|
||||
PageDevMenu
|
||||
};
|
||||
|
||||
@@ -35,6 +35,23 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
||||
#endif
|
||||
}
|
||||
|
||||
QString getPlatformName()
|
||||
{
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
return "Windows";
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
return "Android";
|
||||
#elif defined(Q_OS_LINUX)
|
||||
return "Linux";
|
||||
#elif defined(Q_OS_MACX)
|
||||
return "MacOS";
|
||||
#elif defined(Q_OS_IOS)
|
||||
return "iOS";
|
||||
#else
|
||||
return "Unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
void SettingsController::toggleAmneziaDns(bool enable)
|
||||
{
|
||||
m_settings->setUseAmneziaDns(enable);
|
||||
@@ -130,7 +147,10 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
QJsonObject config = doc.object();
|
||||
|
||||
config["AppPlatform"] = getPlatformName();
|
||||
config["Conf/autoStart"] = Autostart::isAutostart();
|
||||
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
|
||||
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
|
||||
|
||||
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||
}
|
||||
@@ -155,21 +175,41 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
|
||||
}
|
||||
toggleAutoStart(autoStart);
|
||||
#endif
|
||||
|
||||
m_serversModel->resetModel();
|
||||
m_languageModel->changeLanguage(
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
||||
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
|
||||
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
|
||||
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
|
||||
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true";
|
||||
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode);
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
m_appSplitTunnelingModel->setRouteMode(static_cast<int>(Settings::AppsRouteMode::VpnAllExceptApps));
|
||||
#endif
|
||||
|
||||
if (newConfigData.contains("AppPlatform")) { //if backup is from a new version
|
||||
if (newConfigData.value("AppPlatform").toString() != getPlatformName()) {
|
||||
m_appSplitTunnelingModel->clearAppsList();
|
||||
}
|
||||
}
|
||||
|
||||
m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
|
||||
#endif
|
||||
|
||||
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
|
||||
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
|
||||
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true";
|
||||
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
|
||||
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled);
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
m_settings->setAutoConnect(false);
|
||||
m_settings->setStartMinimized(false);
|
||||
m_settings->setKillSwitchEnabled(false);
|
||||
m_settings->setStrictKillSwitchEnabled(false);
|
||||
#endif
|
||||
|
||||
emit restoreBackupFinished();
|
||||
} else {
|
||||
emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
|
||||
@@ -185,8 +225,7 @@ void SettingsController::clearSettings()
|
||||
{
|
||||
m_settings->clearSettings();
|
||||
m_serversModel->resetModel();
|
||||
m_languageModel->changeLanguage(
|
||||
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
|
||||
m_languageModel->changeLanguage(m_languageModel->getSystemLanguageEnum());
|
||||
|
||||
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
|
||||
m_sitesModel->toggleSplitTunneling(false);
|
||||
|
||||
@@ -136,6 +136,8 @@ private:
|
||||
|
||||
QString m_appVersion;
|
||||
|
||||
QString getPlatform();
|
||||
|
||||
QDateTime m_loggingDisableDate;
|
||||
|
||||
bool m_isDevModeEnabled = false;
|
||||
|
||||
@@ -78,6 +78,13 @@ void SitesController::removeSite(int index)
|
||||
emit finished(tr("Site removed: %1").arg(hostname));
|
||||
}
|
||||
|
||||
void SitesController::removeSites()
|
||||
{
|
||||
m_sitesModel->removeSites();
|
||||
|
||||
emit finished(tr("Site list cleared!"));
|
||||
}
|
||||
|
||||
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||
{
|
||||
QByteArray jsonData;
|
||||
|
||||
@@ -19,6 +19,7 @@ public slots:
|
||||
void addSite(QString hostname);
|
||||
void removeSite(int index);
|
||||
|
||||
void removeSites();
|
||||
void importSites(const QString &fileName, bool replaceExisting);
|
||||
void exportSites(const QString &fileName);
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
} else {
|
||||
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. "
|
||||
"Other sites will be opened from your real IP address, "
|
||||
"<a href=\"%1/free\" style=\"color: #FBB26A;\">more details on the website.</a>");
|
||||
"<a href=\"%1\" style=\"color: #FBB26A;\">more details on the website.</a>");
|
||||
}
|
||||
}
|
||||
case PriceRole: {
|
||||
|
||||
@@ -26,12 +26,12 @@ QVariant AppSplitTunnelingModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case AppPathRole: {
|
||||
return m_apps.at(index.row()).appName;
|
||||
}
|
||||
default: {
|
||||
return true;
|
||||
}
|
||||
case AppPathRole: {
|
||||
return m_apps.at(index.row()).appName;
|
||||
}
|
||||
default: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -59,6 +59,13 @@ void AppSplitTunnelingModel::removeApp(QModelIndex index)
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void AppSplitTunnelingModel::clearAppsList() {
|
||||
beginResetModel();
|
||||
m_apps.clear();
|
||||
m_settings->setVpnApps(m_currentRouteMode, m_apps);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int AppSplitTunnelingModel::getRouteMode()
|
||||
{
|
||||
return m_currentRouteMode;
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
public slots:
|
||||
bool addApp(const InstalledAppInfo &appInfo);
|
||||
void removeApp(QModelIndex index);
|
||||
void clearAppsList();
|
||||
|
||||
int getRouteMode();
|
||||
void setRouteMode(int routeMode);
|
||||
|
||||
@@ -101,6 +101,23 @@ QString LanguageModel::getCurrentLanguageName()
|
||||
return m_availableLanguages[getCurrentLanguageIndex()].name;
|
||||
}
|
||||
|
||||
LanguageSettings::AvailableLanguageEnum LanguageModel::getSystemLanguageEnum()
|
||||
{
|
||||
QLocale locale = QLocale::system();
|
||||
switch (locale.language()) {
|
||||
case QLocale::Russian: return LanguageSettings::AvailableLanguageEnum::Russian;
|
||||
case QLocale::Chinese: return LanguageSettings::AvailableLanguageEnum::China_cn;
|
||||
case QLocale::Ukrainian: return LanguageSettings::AvailableLanguageEnum::Ukrainian;
|
||||
case QLocale::Persian: return LanguageSettings::AvailableLanguageEnum::Persian;
|
||||
case QLocale::Arabic: return LanguageSettings::AvailableLanguageEnum::Arabic;
|
||||
case QLocale::Burmese: return LanguageSettings::AvailableLanguageEnum::Burmese;
|
||||
case QLocale::Urdu: return LanguageSettings::AvailableLanguageEnum::Urdu;
|
||||
case QLocale::Hindi: return LanguageSettings::AvailableLanguageEnum::Hindi;
|
||||
case QLocale::English: return LanguageSettings::AvailableLanguageEnum::English;
|
||||
default: return LanguageSettings::AvailableLanguageEnum::English;
|
||||
}
|
||||
}
|
||||
|
||||
QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
||||
{
|
||||
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||
@@ -110,3 +127,13 @@ QString LanguageModel::getCurrentSiteUrl(const QString &path)
|
||||
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||
}
|
||||
}
|
||||
|
||||
QString LanguageModel::getCurrentDocsUrl(const QString &path)
|
||||
{
|
||||
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
|
||||
switch (language) {
|
||||
case LanguageSettings::AvailableLanguageEnum::Russian:
|
||||
return "https://storage.googleapis.com/amnezia/docs" + (path.isEmpty() ? "" : (QString("?m-path=/%1").arg(path)));
|
||||
default: return QString("https://docs.amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(path)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
};
|
||||
|
||||
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
|
||||
LanguageSettings::AvailableLanguageEnum getSystemLanguageEnum();
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
@@ -60,6 +61,7 @@ public slots:
|
||||
int getLineHeightAppend();
|
||||
QString getCurrentLanguageName();
|
||||
QString getCurrentSiteUrl(const QString &path = "");
|
||||
QString getCurrentDocsUrl(const QString &path = "");
|
||||
|
||||
signals:
|
||||
void updateTranslations(const QLocale &locale);
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
#include "ui/models/newsmodel.h"
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QQmlEngine>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QJsonDocument>
|
||||
#include <algorithm>
|
||||
|
||||
NewsModel::NewsModel(const std::shared_ptr<Settings> &settings, QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_settings(settings)
|
||||
{
|
||||
loadReadIds();
|
||||
}
|
||||
|
||||
int NewsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
QVariant NewsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size())
|
||||
return QVariant();
|
||||
|
||||
const NewsItem &item = m_items.at(index.row());
|
||||
switch (role) {
|
||||
case IdRole:
|
||||
return item.id;
|
||||
case TitleRole:
|
||||
return item.title;
|
||||
case ContentRole:
|
||||
return item.content;
|
||||
case TimestampRole:
|
||||
return item.timestamp.toString(Qt::ISODate);
|
||||
case IsReadRole:
|
||||
return item.read;
|
||||
case IsProcessedRole:
|
||||
return index.row() == m_processedIndex;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> NewsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[IdRole] = "id";
|
||||
roles[TitleRole] = "title";
|
||||
roles[ContentRole] = "content";
|
||||
roles[TimestampRole] = "timestamp";
|
||||
roles[IsReadRole] = "read";
|
||||
roles[IsProcessedRole] = "isProcessed";
|
||||
return roles;
|
||||
}
|
||||
|
||||
void NewsModel::markAsRead(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_items.size())
|
||||
return;
|
||||
if (!m_items[index].read) {
|
||||
m_items[index].read = true;
|
||||
m_readIds.insert(m_items[index].id);
|
||||
saveReadIds();
|
||||
QModelIndex idx = createIndex(index, 0);
|
||||
emit dataChanged(idx, idx, {IsReadRole});
|
||||
emit hasUnreadChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int NewsModel::processedIndex() const
|
||||
{
|
||||
return m_processedIndex;
|
||||
}
|
||||
|
||||
void NewsModel::setProcessedIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_items.size() || m_processedIndex == index)
|
||||
return;
|
||||
m_processedIndex = index;
|
||||
emit processedIndexChanged(index);
|
||||
}
|
||||
|
||||
void NewsModel::updateModel(const QJsonArray &serverItems)
|
||||
{
|
||||
QSet<QString> existingIds;
|
||||
for (const NewsItem &item : m_items) {
|
||||
existingIds.insert(item.id);
|
||||
}
|
||||
|
||||
QList<NewsItem> newItems;
|
||||
for (const QJsonValue &value : serverItems) {
|
||||
if (!value.isObject()) continue;
|
||||
const QJsonObject obj = value.toObject();
|
||||
QString id = obj.value("id").toString();
|
||||
|
||||
if (!existingIds.contains(id)) {
|
||||
NewsItem item;
|
||||
item.id = id;
|
||||
item.title = obj.value("title").toString();
|
||||
item.content = obj.value("content").toString();
|
||||
item.timestamp = QDateTime::fromString(obj.value("timestamp").toString(), Qt::ISODate);
|
||||
item.read = m_readIds.contains(id);
|
||||
newItems.append(item);
|
||||
existingIds.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newItems.isEmpty()) {
|
||||
beginResetModel();
|
||||
m_items.append(newItems);
|
||||
// Sort descending by timestamp (newest first)
|
||||
std::sort(m_items.begin(), m_items.end(), [](const NewsItem &a, const NewsItem &b) {
|
||||
return a.timestamp > b.timestamp;
|
||||
});
|
||||
endResetModel();
|
||||
emit hasUnreadChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool NewsModel::hasUnread() const
|
||||
{
|
||||
for (const NewsItem &item : m_items) {
|
||||
if (!item.read)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NewsModel::loadReadIds()
|
||||
{
|
||||
QStringList ids = m_settings->readNewsIds();
|
||||
m_readIds = QSet<QString>(ids.begin(), ids.end());
|
||||
}
|
||||
|
||||
void NewsModel::saveReadIds() const
|
||||
{
|
||||
m_settings->setReadNewsIds(QStringList(m_readIds.begin(), m_readIds.end()));
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef NEWSMODEL_H
|
||||
#define NEWSMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QJsonArray>
|
||||
#include <QSet>
|
||||
#include <memory>
|
||||
#include "settings.h"
|
||||
|
||||
struct NewsItem {
|
||||
QString id;
|
||||
QString title;
|
||||
QString content;
|
||||
QDateTime timestamp;
|
||||
bool read;
|
||||
};
|
||||
|
||||
class NewsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Roles {
|
||||
IdRole = Qt::UserRole + 1,
|
||||
TitleRole,
|
||||
ContentRole,
|
||||
TimestampRole,
|
||||
IsReadRole,
|
||||
IsProcessedRole
|
||||
};
|
||||
explicit NewsModel(const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
|
||||
Q_INVOKABLE void markAsRead(int index);
|
||||
|
||||
Q_PROPERTY(int processedIndex READ processedIndex WRITE setProcessedIndex NOTIFY processedIndexChanged)
|
||||
Q_PROPERTY(bool hasUnread READ hasUnread NOTIFY hasUnreadChanged)
|
||||
int processedIndex() const;
|
||||
void setProcessedIndex(int index);
|
||||
|
||||
void updateModel(const QJsonArray &items);
|
||||
bool hasUnread() const;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
signals:
|
||||
void processedIndexChanged(int index);
|
||||
void hasUnreadChanged();
|
||||
|
||||
private:
|
||||
QVector<NewsItem> m_items;
|
||||
int m_processedIndex = -1;
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
QSet<QString> m_readIds;
|
||||
void loadReadIds();
|
||||
void saveReadIds() const;
|
||||
};
|
||||
|
||||
#endif // NEWSMODEL_H
|
||||
@@ -2,72 +2,128 @@
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
|
||||
OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent) : QAbstractListModel(parent)
|
||||
OpenVpnConfigModel::OpenVpnConfigModel(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int OpenVpnConfigModel::rowCount(const QModelIndex &parent) const
|
||||
QString OpenVpnConfigModel::subnetAddress() const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 1;
|
||||
return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress);
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
void OpenVpnConfigModel::setSubnetAddress(const QString &subnetAddress)
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= ContainerProps::allContainers().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::SubnetAddressRole: m_protocolConfig.insert(amnezia::config_key::subnet_address, value.toString()); break;
|
||||
case Roles::TransportProtoRole: m_protocolConfig.insert(config_key::transport_proto, value.toString()); break;
|
||||
case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break;
|
||||
case Roles::AutoNegotiateEncryprionRole: m_protocolConfig.insert(config_key::ncp_disable, !value.toBool()); break;
|
||||
case Roles::HashRole: m_protocolConfig.insert(config_key::hash, value.toString()); break;
|
||||
case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break;
|
||||
case Roles::TlsAuthRole: m_protocolConfig.insert(config_key::tls_auth, value.toBool()); break;
|
||||
case Roles::BlockDnsRole: m_protocolConfig.insert(config_key::block_outside_dns, value.toBool()); break;
|
||||
case Roles::AdditionalClientCommandsRole: m_protocolConfig.insert(config_key::additional_client_config, value.toString()); break;
|
||||
case Roles::AdditionalServerCommandsRole: m_protocolConfig.insert(config_key::additional_server_config, value.toString()); break;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QList { role });
|
||||
return true;
|
||||
m_protocolConfig.insert(amnezia::config_key::subnet_address, subnetAddress);
|
||||
}
|
||||
|
||||
QVariant OpenVpnConfigModel::data(const QModelIndex &index, int role) const
|
||||
QString OpenVpnConfigModel::transportProto() const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
|
||||
return false;
|
||||
}
|
||||
return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::SubnetAddressRole:
|
||||
return m_protocolConfig.value(amnezia::config_key::subnet_address).toString(amnezia::protocols::openvpn::defaultSubnetAddress);
|
||||
case Roles::TransportProtoRole:
|
||||
return m_protocolConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto);
|
||||
case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort);
|
||||
case Roles::AutoNegotiateEncryprionRole:
|
||||
return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||
case Roles::HashRole: return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash);
|
||||
case Roles::CipherRole: return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
|
||||
case Roles::TlsAuthRole: return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||
case Roles::BlockDnsRole:
|
||||
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
|
||||
case Roles::AdditionalClientCommandsRole:
|
||||
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
|
||||
case Roles::AdditionalServerCommandsRole:
|
||||
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
|
||||
case Roles::IsPortEditable: return m_container == DockerContainer::OpenVpn ? true : false;
|
||||
case Roles::IsTransportProtoEditable: return m_container == DockerContainer::OpenVpn ? true : false;
|
||||
case Roles::HasRemoveButton: return m_container == DockerContainer::OpenVpn ? true : false;
|
||||
}
|
||||
return QVariant();
|
||||
void OpenVpnConfigModel::setTransportProto(const QString &transportProto)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::transport_proto, transportProto);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigModel::port() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::port).toString(protocols::openvpn::defaultPort);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setPort(const QString &port)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::port, port);
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::autoNegotiateEncryption() const
|
||||
{
|
||||
return !m_protocolConfig.value(config_key::ncp_disable).toBool(protocols::openvpn::defaultNcpDisable);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setAutoNegotiateEncryption(bool enabled)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::ncp_disable, !enabled);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigModel::hash() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::hash).toString(protocols::openvpn::defaultHash);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setHash(const QString &hash)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::hash, hash);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigModel::cipher() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::cipher).toString(protocols::openvpn::defaultCipher);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setCipher(const QString &cipher)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::cipher, cipher);
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::tlsAuth() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setTlsAuth(bool enabled)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::tls_auth, enabled);
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::blockDns() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::block_outside_dns).toBool(protocols::openvpn::defaultBlockOutsideDns);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setBlockDns(bool enabled)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::block_outside_dns, enabled);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigModel::additionalClientCommands() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::additional_client_config).toString(protocols::openvpn::defaultAdditionalClientConfig);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setAdditionalClientCommands(const QString &commands)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::additional_client_config, commands);
|
||||
}
|
||||
|
||||
QString OpenVpnConfigModel::additionalServerCommands() const
|
||||
{
|
||||
return m_protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig);
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::setAdditionalServerCommands(const QString &commands)
|
||||
{
|
||||
m_protocolConfig.insert(config_key::additional_server_config, commands);
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::isPortEditable() const
|
||||
{
|
||||
return m_container == DockerContainer::OpenVpn;
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::isTransportProtoEditable() const
|
||||
{
|
||||
return m_container == DockerContainer::OpenVpn;
|
||||
}
|
||||
|
||||
bool OpenVpnConfigModel::hasRemoveButton() const
|
||||
{
|
||||
return m_container == DockerContainer::OpenVpn;
|
||||
}
|
||||
|
||||
void OpenVpnConfigModel::updateModel(const QJsonObject &config)
|
||||
{
|
||||
beginResetModel();
|
||||
m_container = ContainerProps::containerFromString(config.value(config_key::container).toString());
|
||||
|
||||
m_fullConfig = config;
|
||||
@@ -100,8 +156,6 @@ void OpenVpnConfigModel::updateModel(const QJsonObject &config)
|
||||
m_protocolConfig.insert(
|
||||
config_key::additional_server_config,
|
||||
protocolConfig.value(config_key::additional_server_config).toString(protocols::openvpn::defaultAdditionalServerConfig));
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QJsonObject OpenVpnConfigModel::getConfig()
|
||||
@@ -109,26 +163,3 @@ QJsonObject OpenVpnConfigModel::getConfig()
|
||||
m_fullConfig.insert(config_key::openvpn, m_protocolConfig);
|
||||
return m_fullConfig;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> OpenVpnConfigModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[SubnetAddressRole] = "subnetAddress";
|
||||
roles[TransportProtoRole] = "transportProto";
|
||||
roles[PortRole] = "port";
|
||||
roles[AutoNegotiateEncryprionRole] = "autoNegotiateEncryprion";
|
||||
roles[HashRole] = "hash";
|
||||
roles[CipherRole] = "cipher";
|
||||
roles[TlsAuthRole] = "tlsAuth";
|
||||
roles[BlockDnsRole] = "blockDns";
|
||||
roles[AdditionalClientCommandsRole] = "additionalClientCommands";
|
||||
roles[AdditionalServerCommandsRole] = "additionalServerCommands";
|
||||
|
||||
roles[IsPortEditable] = "isPortEditable";
|
||||
roles[IsTransportProtoEditable] = "isTransportProtoEditable";
|
||||
|
||||
roles[HasRemoveButton] = "hasRemoveButton";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -1,47 +1,90 @@
|
||||
#ifndef OPENVPNCONFIGMODEL_H
|
||||
#define OPENVPNCONFIGMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "containers/containers_defs.h"
|
||||
|
||||
class OpenVpnConfigModel : public QAbstractListModel
|
||||
class OpenVpnConfigModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString subnetAddress READ subnetAddress WRITE setSubnetAddress NOTIFY subnetAddressChanged)
|
||||
Q_PROPERTY(QString transportProto READ transportProto WRITE setTransportProto NOTIFY transportProtoChanged)
|
||||
Q_PROPERTY(QString port READ port WRITE setPort NOTIFY portChanged)
|
||||
Q_PROPERTY(bool autoNegotiateEncryption READ autoNegotiateEncryption WRITE setAutoNegotiateEncryption NOTIFY autoNegotiateEncryptionChanged)
|
||||
Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY hashChanged)
|
||||
Q_PROPERTY(QString cipher READ cipher WRITE setCipher NOTIFY cipherChanged)
|
||||
Q_PROPERTY(bool tlsAuth READ tlsAuth WRITE setTlsAuth NOTIFY tlsAuthChanged)
|
||||
Q_PROPERTY(bool blockDns READ blockDns WRITE setBlockDns NOTIFY blockDnsChanged)
|
||||
Q_PROPERTY(QString additionalClientCommands READ additionalClientCommands WRITE setAdditionalClientCommands NOTIFY additionalClientCommandsChanged)
|
||||
Q_PROPERTY(QString additionalServerCommands READ additionalServerCommands WRITE setAdditionalServerCommands NOTIFY additionalServerCommandsChanged)
|
||||
Q_PROPERTY(bool isPortEditable READ isPortEditable NOTIFY isPortEditableChanged)
|
||||
Q_PROPERTY(bool isTransportProtoEditable READ isTransportProtoEditable NOTIFY isTransportProtoEditableChanged)
|
||||
Q_PROPERTY(bool hasRemoveButton READ hasRemoveButton NOTIFY hasRemoveButtonChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
SubnetAddressRole = Qt::UserRole + 1,
|
||||
TransportProtoRole,
|
||||
PortRole,
|
||||
AutoNegotiateEncryprionRole,
|
||||
HashRole,
|
||||
CipherRole,
|
||||
TlsAuthRole,
|
||||
BlockDnsRole,
|
||||
AdditionalClientCommandsRole,
|
||||
AdditionalServerCommandsRole,
|
||||
|
||||
IsPortEditable,
|
||||
IsTransportProtoEditable,
|
||||
|
||||
HasRemoveButton
|
||||
};
|
||||
|
||||
explicit OpenVpnConfigModel(QObject *parent = nullptr);
|
||||
~OpenVpnConfigModel() override = default;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
OpenVpnConfigModel(const OpenVpnConfigModel &) = delete;
|
||||
OpenVpnConfigModel &operator=(const OpenVpnConfigModel &) = delete;
|
||||
OpenVpnConfigModel(OpenVpnConfigModel &&) = delete;
|
||||
OpenVpnConfigModel &operator=(OpenVpnConfigModel &&) = delete;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QString subnetAddress() const;
|
||||
void setSubnetAddress(const QString &subnetAddress);
|
||||
|
||||
QString transportProto() const;
|
||||
void setTransportProto(const QString &transportProto);
|
||||
|
||||
QString port() const;
|
||||
void setPort(const QString &port);
|
||||
|
||||
bool autoNegotiateEncryption() const;
|
||||
void setAutoNegotiateEncryption(bool enabled);
|
||||
|
||||
QString hash() const;
|
||||
void setHash(const QString &hash);
|
||||
|
||||
QString cipher() const;
|
||||
void setCipher(const QString &cipher);
|
||||
|
||||
bool tlsAuth() const;
|
||||
void setTlsAuth(bool enabled);
|
||||
|
||||
bool blockDns() const;
|
||||
void setBlockDns(bool enabled);
|
||||
|
||||
QString additionalClientCommands() const;
|
||||
void setAdditionalClientCommands(const QString &commands);
|
||||
|
||||
QString additionalServerCommands() const;
|
||||
void setAdditionalServerCommands(const QString &commands);
|
||||
|
||||
bool isPortEditable() const;
|
||||
bool isTransportProtoEditable() const;
|
||||
bool hasRemoveButton() const;
|
||||
|
||||
Q_INVOKABLE QJsonObject getConfig();
|
||||
|
||||
signals:
|
||||
void subnetAddressChanged(const QString &);
|
||||
void transportProtoChanged(const QString &);
|
||||
void portChanged(const QString &);
|
||||
void autoNegotiateEncryptionChanged(bool);
|
||||
void hashChanged(const QString &);
|
||||
void cipherChanged(const QString &);
|
||||
void tlsAuthChanged(bool);
|
||||
void blockDnsChanged(bool);
|
||||
void additionalClientCommandsChanged(const QString &);
|
||||
void additionalServerCommandsChanged(const QString &);
|
||||
void isPortEditableChanged(bool);
|
||||
void isTransportProtoEditableChanged(bool);
|
||||
void hasRemoveButtonChanged(bool);
|
||||
|
||||
public slots:
|
||||
void updateModel(const QJsonObject &config);
|
||||
QJsonObject getConfig();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
DockerContainer m_container;
|
||||
|
||||
@@ -83,6 +83,16 @@ void SitesModel::removeSite(QModelIndex index)
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void SitesModel::removeSites()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_settings->removeAllVpnSites(m_currentRouteMode);
|
||||
fillSites();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int SitesModel::getRouteMode()
|
||||
{
|
||||
return m_currentRouteMode;
|
||||
|
||||
@@ -28,6 +28,7 @@ public slots:
|
||||
bool addSite(const QString &hostname, const QString &ip);
|
||||
void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
|
||||
void removeSite(QModelIndex index);
|
||||
void removeSites();
|
||||
|
||||
int getRouteMode();
|
||||
void setRouteMode(int routeMode);
|
||||
|
||||
@@ -10,8 +10,7 @@ import ProtocolEnum 1.0
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: menuContent
|
||||
|
||||
property var rootWidth
|
||||
@@ -21,13 +20,6 @@ ListView {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
clip: true
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ButtonGroup {
|
||||
id: containersRadioButtonGroup
|
||||
}
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
|
||||
text: qsTr("Split tunneling on the server")
|
||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: siteBasedSplitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: appSplitTunnelingSwitch
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("App-based split tunneling")
|
||||
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
|
||||
text: qsTr("Split tunneling on the server")
|
||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: siteBasedSplitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: appSplitTunnelingSwitch
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("App-based split tunneling")
|
||||
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ DrawerType2 {
|
||||
headerText: qsTr("Choose application")
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -66,11 +66,6 @@ DrawerType2 {
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyInstalledAppsModel
|
||||
sourceModel: installedAppsModel
|
||||
@@ -81,44 +76,35 @@ DrawerType2 {
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: root.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
RowLayout {
|
||||
CheckBoxType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
CheckBoxType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: appName
|
||||
checked: isAppSelected
|
||||
onCheckedChanged: {
|
||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "image://installedAppImage/" + appIcon
|
||||
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
|
||||
Layout.rightMargin: 48
|
||||
text: appName
|
||||
checked: isAppSelected
|
||||
onCheckedChanged: {
|
||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
Image {
|
||||
source: "image://installedAppImage/" + appIcon
|
||||
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
|
||||
Layout.rightMargin: 48
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ DrawerType2 {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
@@ -57,14 +57,8 @@ DrawerType2 {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
property bool isFocusable: true
|
||||
property int selectedIndex: LanguageModel.currentLanguageIndex
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
model: LanguageModel
|
||||
|
||||
ButtonGroup {
|
||||
|
||||
@@ -15,7 +15,7 @@ import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
property int selectedIndex: ServersModel.defaultIndex
|
||||
@@ -28,10 +28,6 @@ ListView {
|
||||
|
||||
model: ServersModel
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Connections {
|
||||
target: ServersModel
|
||||
function onDefaultServerIndexChanged(serverIndex) {
|
||||
@@ -39,9 +35,6 @@ ListView {
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
delegate: Item {
|
||||
id: menuContentDelegate
|
||||
objectName: "menuContentDelegate"
|
||||
|
||||
@@ -13,78 +13,64 @@ import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
width: parent.width
|
||||
height: root.contentItem.height
|
||||
anchors.fill: parent
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
delegate: ColumnLayout {
|
||||
width: root.width
|
||||
|
||||
property bool isFocusable: false
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: root.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
text: name
|
||||
descriptionText: description
|
||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
clickedFunction: function() {
|
||||
if (isInstalled) {
|
||||
var containerIndex = root.model.mapToSource(index)
|
||||
ContainersModel.setProcessedContainerIndex(containerIndex)
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
LabelWithButtonType {
|
||||
id: containerRadioButton
|
||||
implicitWidth: parent.width
|
||||
|
||||
text: name
|
||||
descriptionText: description
|
||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isInstalled) {
|
||||
var containerIndex = root.model.mapToSource(index)
|
||||
ContainersModel.setProcessedContainerIndex(containerIndex)
|
||||
|
||||
if (serviceType !== ProtocolEnum.Other) {
|
||||
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch (containerIndex) {
|
||||
case ContainerEnum.Ipsec: {
|
||||
if (serviceType !== ProtocolEnum.Other) {
|
||||
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
break
|
||||
return
|
||||
}
|
||||
case ContainerEnum.Dns: {
|
||||
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
||||
InstallController.setShouldCreateServer(false)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
switch (containerIndex) {
|
||||
case ContainerEnum.Ipsec: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
break
|
||||
}
|
||||
case ContainerEnum.Dns: {
|
||||
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
||||
InstallController.setShouldCreateServer(false)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import ContainerProps 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string configContentHeaderText
|
||||
property string shareButtonText: qsTr("Share")
|
||||
property string copyButtonText: qsTr("Copy")
|
||||
property bool isSelfHostedConfig: true
|
||||
|
||||
property string configExtension: ".vpn"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
property string configFileName: "amnezia_config"
|
||||
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
onClosed: {
|
||||
configExtension = ".vpn"
|
||||
configCaption = qsTr("Save AmneziaVPN config")
|
||||
configFileName = "amnezia_config"
|
||||
}
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: root.expandedHeight
|
||||
|
||||
Header2Type {
|
||||
id: header
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
headerText: root.headerText
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
model: 1
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: shareButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: root.shareButtonText
|
||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
||||
|
||||
clickedFunc: function() {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = configFileName + configExtension
|
||||
} else {
|
||||
fileName = SystemController.getFileName(configCaption,
|
||||
qsTr("Config files (*" + configExtension + ")"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + configFileName,
|
||||
true,
|
||||
configExtension)
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
ExportController.exportConfig(fileName)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: copyConfigTextButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: root.copyButtonText
|
||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
||||
|
||||
Keys.onReturnPressed: { copyConfigTextButton.clicked() }
|
||||
Keys.onEnterPressed: { copyConfigTextButton.clicked() }
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: copyNativeConfigStringButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: false
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Copy config string")
|
||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
||||
|
||||
KeyNavigation.tab: showSettingsButton
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: showSettingsButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: root.isSelfHostedConfig
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Show connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
configContentDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: configContentDrawer
|
||||
|
||||
parent: root.parent
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
expandedStateContent: Item {
|
||||
id: configContentContainer
|
||||
|
||||
implicitHeight: configContentDrawer.expandedHeight
|
||||
|
||||
Connections {
|
||||
target: copyNativeConfigStringButton
|
||||
function onClicked() {
|
||||
nativeConfigString.selectAll()
|
||||
nativeConfigString.copy()
|
||||
nativeConfigString.select(0, 0)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: copyConfigTextButton
|
||||
function onClicked() {
|
||||
configText.selectAll()
|
||||
configText.copy()
|
||||
configText.select(0, 0)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
header.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() { configContentDrawer.closeTriggered() }
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.top: backButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
|
||||
|
||||
ColumnLayout {
|
||||
id: configContent
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
Header2Type {
|
||||
id: configContentHeader
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
headerText: root.configContentHeaderText
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: nativeConfigString
|
||||
visible: false
|
||||
text: ExportController.nativeConfigString
|
||||
|
||||
onTextChanged: {
|
||||
copyNativeConfigStringButton.visible = nativeConfigString.text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: configText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
padding: 0
|
||||
leftPadding: 0
|
||||
height: 24
|
||||
|
||||
readOnly: true
|
||||
activeFocusOnTab: false
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
text: ExportController.config
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property bool isQrCodeVisible: root.isSelfHostedConfig ? ExportController.qrCodesCount > 0 : ApiConfigsController.qrCodesCount > 0
|
||||
|
||||
Rectangle {
|
||||
id: qrCodeContainer
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: isQrCodeVisible
|
||||
|
||||
color: "white"
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
smooth: false
|
||||
|
||||
source: root.isSelfHostedConfig ? (isQrCodeVisible ? ExportController.qrCodes[0] : "") :
|
||||
(isQrCodeVisible ? ApiConfigsController.qrCodes[0] : "")
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
Timer {
|
||||
property int index: 0
|
||||
interval: 1000
|
||||
running: isQrCodeVisible
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (isQrCodeVisible) {
|
||||
index++
|
||||
let qrCodesCount = root.isSelfHostedConfig ? ExportController.qrCodesCount : ApiConfigsController.qrCodesCount
|
||||
if (index >= qrCodesCount) {
|
||||
index = 0
|
||||
}
|
||||
|
||||
parent.source = root.isSelfHostedConfig ? ExportController.qrCodes[index] : ApiConfigsController.qrCodes[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on source {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: isQrCodeVisible
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("To read the QR code in the Amnezia app, select \"Add server\" → \"I have data to connect\" → \"QR code, key or settings file\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +1,210 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
||||
property string defaultColor: AmneziaStyle.color.paleGray
|
||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string borderColor: AmneziaStyle.color.paleGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderWidth: 0
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string leftImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageColor: textColor
|
||||
property bool changeLeftImageSize: true
|
||||
|
||||
property bool squareLeftSide: false
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
property var clickedFunc
|
||||
|
||||
property alias buttonTextLabel: buttonText
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitHeight: 56
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: focusBorder
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
radius: 16
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: focusBorder
|
||||
anchors.margins: root.activeFocus ? 2 : 0
|
||||
|
||||
radius: root.activeFocus ? 14 : 16
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
border.color: borderColor
|
||||
border.width: borderWidth
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.squareLeftSide
|
||||
|
||||
z: 1
|
||||
|
||||
width: parent.radius
|
||||
height: parent.radius
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: focusBorder
|
||||
enabled: false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: focusBorder
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: root.leftImageSource
|
||||
visible: root.leftImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: leftImageColor !== "" ? true : false
|
||||
effect: ColorOverlay {
|
||||
color: leftImageColor
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.changeLeftImageSize) {
|
||||
leftImage.Layout.preferredHeight = 20
|
||||
leftImage.Layout.preferredWidth = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
id: buttonText
|
||||
|
||||
color: root.textColor
|
||||
text: root.text
|
||||
visible: root.text === "" ? false : true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
|
||||
source: root.rightImageSource
|
||||
visible: root.rightImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
||||
property string defaultColor: AmneziaStyle.color.paleGray
|
||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string borderColor: AmneziaStyle.color.paleGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderWidth: 0
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string leftImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageColor: textColor
|
||||
property bool changeLeftImageSize: true
|
||||
|
||||
property bool squareLeftSide: false
|
||||
|
||||
property var clickedFunc
|
||||
|
||||
property alias buttonTextLabel: buttonText
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitHeight: 56
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: focusBorder
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
radius: 16
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: focusBorder
|
||||
anchors.margins: root.activeFocus ? 2 : 0
|
||||
|
||||
radius: root.activeFocus ? 14 : 16
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
border.color: borderColor
|
||||
border.width: borderWidth
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.squareLeftSide
|
||||
|
||||
z: 1
|
||||
|
||||
width: parent.radius
|
||||
height: parent.radius
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: focusBorder
|
||||
enabled: false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: focusBorder
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: root.leftImageSource
|
||||
visible: root.leftImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: leftImageColor !== "" ? true : false
|
||||
effect: ColorOverlay {
|
||||
color: leftImageColor
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.changeLeftImageSize) {
|
||||
leftImage.Layout.preferredHeight = 20
|
||||
leftImage.Layout.preferredWidth = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
id: buttonText
|
||||
|
||||
color: root.textColor
|
||||
text: root.text
|
||||
visible: root.text === "" ? false : true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
|
||||
source: root.rightImageSource
|
||||
visible: root.rightImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,203 +1,185 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string bodyText
|
||||
property string footerText
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string rightImageSource
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string leftImageSource
|
||||
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property alias focusItem: rightImage
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
|
||||
color: defaultColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
function ensureVisible(item) {
|
||||
if (item.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
ensureVisible(root)
|
||||
}
|
||||
|
||||
focusItem.onFocusChanged: {
|
||||
root.ensureVisible(focusItem)
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: leftImageSource
|
||||
|
||||
visible: leftImageSource !== ""
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 24
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.headerText
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
text: root.bodyText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
textFormat: Text.RichText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
text: root.footerText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
enabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
backgroundRect.color = root.hoveredColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
backgroundRect.color = root.defaultColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string bodyText
|
||||
property string footerText
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string rightImageSource
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string leftImageSource
|
||||
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property alias focusItem: rightImage
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
|
||||
color: defaultColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: leftImageSource
|
||||
|
||||
visible: leftImageSource !== ""
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 24
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.headerText
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
text: root.bodyText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
textFormat: Text.RichText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
text: root.footerText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
enabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
backgroundRect.color = root.hoveredColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
backgroundRect.color = root.defaultColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,170 +1,187 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
CheckBox {
|
||||
id: root
|
||||
|
||||
property string descriptionText
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
property string defaultColor: AmneziaStyle.color.transparent
|
||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
|
||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
||||
|
||||
property string imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
property var parentFlickable
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.NoFocus
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
radius: 16
|
||||
|
||||
color: {
|
||||
if (root.hovered) {
|
||||
return hoveredColor
|
||||
}
|
||||
return defaultColor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: imageBorder
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 24
|
||||
height: 24
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.checked ?
|
||||
(root.enabled ?
|
||||
checkedBorderColor :
|
||||
checkedBorderDisabledColor) :
|
||||
defaultBorderColor
|
||||
border.width: 1
|
||||
radius: 4
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
|
||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (root.pressed) {
|
||||
return root.pressedImageColor
|
||||
} else if (root.checked) {
|
||||
if (root.enabled) {
|
||||
return root.checkedImageColor
|
||||
} else {
|
||||
return root.checkedDisabledImageColor
|
||||
}
|
||||
} else {
|
||||
return root.defaultImageColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 8 + background.width
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
spacing: 4
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.descriptionText
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
CheckBox {
|
||||
id: root
|
||||
|
||||
property string descriptionText
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
property string defaultColor: AmneziaStyle.color.transparent
|
||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
|
||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
||||
|
||||
property string imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.NoFocus
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
radius: 16
|
||||
|
||||
color: {
|
||||
if (root.hovered) {
|
||||
return hoveredColor
|
||||
}
|
||||
return defaultColor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: imageBorder
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 24
|
||||
height: 24
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.checked ?
|
||||
(root.enabled ?
|
||||
checkedBorderColor :
|
||||
checkedBorderDisabledColor) :
|
||||
defaultBorderColor
|
||||
border.width: 1
|
||||
radius: 4
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
|
||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (root.pressed) {
|
||||
return root.pressedImageColor
|
||||
} else if (root.checked) {
|
||||
if (root.enabled) {
|
||||
return root.checkedImageColor
|
||||
} else {
|
||||
return root.checkedDisabledImageColor
|
||||
}
|
||||
} else {
|
||||
return root.defaultImageColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 8 + background.width
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
spacing: 4
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.descriptionText
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,5 @@ Flickable {
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {
|
||||
id: scrollBar
|
||||
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,328 +1,309 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string text
|
||||
property int textMaximumLineCount: 2
|
||||
property int textElide: Qt.ElideRight
|
||||
|
||||
property string descriptionText
|
||||
|
||||
property var clickedFunction
|
||||
|
||||
property string buttonImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageSource
|
||||
property bool isLeftImageHoverEnabled: true
|
||||
property bool isSmallLeftImage: false
|
||||
|
||||
property alias rightButton: rightImage
|
||||
property alias eyeButton: eyeImage
|
||||
property FlickableType parentFlickable
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
|
||||
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rightImage
|
||||
function onFocusChanged() {
|
||||
if (rightImage.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: leftImageBackground
|
||||
|
||||
visible: leftImageSource ? true : false
|
||||
|
||||
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
|
||||
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
|
||||
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
|
||||
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: leftImageSource
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
property real textLineHeight: 21.6
|
||||
property real descriptionTextLineHeight: 16
|
||||
|
||||
property int textPixelSize: 18
|
||||
property int descriptionTextSize: 13
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.text
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.descriptionColor : root.textColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
maximumLineCount: root.textMaximumLineCount
|
||||
elide: root.textElide
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
function replaceWithAsterisks(input) {
|
||||
return '*'.repeat(input.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: eyeImage
|
||||
visible: buttonImageSource !== ""
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: true
|
||||
image: buttonImageSource
|
||||
imageColor: rightImageColor
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: eyeImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hideDescription = !hideDescription
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
clicked()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
clicked()
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: root
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// property alias focusObjectName: eyeImage.objectName
|
||||
property string text
|
||||
property int textMaximumLineCount: 2
|
||||
property int textElide: Qt.ElideRight
|
||||
|
||||
property string descriptionText
|
||||
|
||||
property var clickedFunction
|
||||
|
||||
property string buttonImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageSource
|
||||
property bool isLeftImageHoverEnabled: true
|
||||
property bool isSmallLeftImage: false
|
||||
|
||||
property alias rightButton: rightImage
|
||||
property alias eyeButton: eyeImage
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
|
||||
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: leftImageBackground
|
||||
|
||||
visible: leftImageSource ? true : false
|
||||
|
||||
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
|
||||
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
|
||||
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
|
||||
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: leftImageSource
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
property real textLineHeight: 21.6
|
||||
property real descriptionTextLineHeight: 16
|
||||
|
||||
property int textPixelSize: 18
|
||||
property int descriptionTextSize: 13
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.text
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.descriptionColor : root.textColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
maximumLineCount: root.textMaximumLineCount
|
||||
elide: root.textElide
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
function replaceWithAsterisks(input) {
|
||||
return '*'.repeat(input.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: eyeImage
|
||||
visible: buttonImageSource !== ""
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: true
|
||||
image: buttonImageSource
|
||||
imageColor: rightImageColor
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: eyeImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hideDescription = !hideDescription
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
clicked()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
clicked()
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: root
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,33 +6,16 @@ ListView {
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
function findChildWithObjectName(items, name) {
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
if (items[i].objectName === name)
|
||||
return items[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
property var rootWidth
|
||||
@@ -25,13 +25,6 @@ ListView {
|
||||
width: rootWidth
|
||||
height: root.contentItem.height
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ import "../Controls2"
|
||||
ScrollBar {
|
||||
id: root
|
||||
|
||||
policy: parent.height >= parent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
||||
policy: ScrollBar.AsNeeded
|
||||
}
|
||||
|
||||
@@ -1,172 +1,162 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
property alias descriptionText: description.text
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string checkedIndicatorColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
|
||||
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
|
||||
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
|
||||
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
|
||||
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
|
||||
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.TabFocus
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: switcher
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 52
|
||||
implicitHeight: 32
|
||||
|
||||
radius: 16
|
||||
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
|
||||
: root.defaultIndicatorColor
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
|
||||
: root.defaultIndicatorBorderColor)
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: innerCircle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.checked ? parent.width - width - 4 : 8
|
||||
width: root.checked ? 24 : 16
|
||||
height: root.checked ? 24 : 16
|
||||
radius: 23
|
||||
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
|
||||
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
|
||||
|
||||
Behavior on x {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: innerCircle
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 23
|
||||
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => handleSwitch(event)
|
||||
Keys.onReturnPressed: event => handleSwitch(event)
|
||||
Keys.onSpacePressed: event => handleSwitch(event)
|
||||
|
||||
function handleSwitch(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
root.checked = !root.checked
|
||||
root.checkedChanged()
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
property alias descriptionText: description.text
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string checkedIndicatorColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
|
||||
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
|
||||
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
|
||||
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
|
||||
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
|
||||
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.TabFocus
|
||||
|
||||
indicator: Rectangle {
|
||||
id: switcher
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 52
|
||||
implicitHeight: 32
|
||||
|
||||
radius: 16
|
||||
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
|
||||
: root.defaultIndicatorColor
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
|
||||
: root.defaultIndicatorBorderColor)
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: innerCircle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.checked ? parent.width - width - 4 : 8
|
||||
width: root.checked ? 24 : 16
|
||||
height: root.checked ? 24 : 16
|
||||
radius: 23
|
||||
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
|
||||
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
|
||||
|
||||
Behavior on x {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: innerCircle
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 23
|
||||
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => handleSwitch(event)
|
||||
Keys.onReturnPressed: event => handleSwitch(event)
|
||||
Keys.onSpacePressed: event => handleSwitch(event)
|
||||
|
||||
function handleSwitch(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
root.checked = !root.checked
|
||||
root.checkedChanged()
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +1,135 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Style 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
interactive: false
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: textArea.implicitHeight
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding: 16
|
||||
leftPadding: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Style 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
interactive: false
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: textArea.implicitHeight
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding: 16
|
||||
leftPadding: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,180 +1,171 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property string headerText
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string firstButtonImage
|
||||
property string secondButtonImage
|
||||
|
||||
property var firstButtonClickedFunc
|
||||
property var secondButtonClickedFunc
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 0
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.headerText
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
leftPadding: 0
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -8
|
||||
spacing: 0
|
||||
ImageButtonType {
|
||||
id: firstButton
|
||||
visible: root.firstButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.firstButtonImage
|
||||
onClicked: function() {
|
||||
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
|
||||
root.firstButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: secondButton
|
||||
visible: root.secondButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.secondButtonImage
|
||||
onClicked: function() {
|
||||
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
|
||||
root.secondButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: resetButton
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
visible: root.textAreaText !== ""
|
||||
image: "qrc:/images/controls/close.svg"
|
||||
|
||||
onClicked: function() {
|
||||
root.textAreaText = ""
|
||||
textArea.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property string headerText
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string firstButtonImage
|
||||
property string secondButtonImage
|
||||
|
||||
property var firstButtonClickedFunc
|
||||
property var secondButtonClickedFunc
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 0
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.headerText
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
leftPadding: 0
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -8
|
||||
spacing: 0
|
||||
ImageButtonType {
|
||||
id: firstButton
|
||||
visible: root.firstButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.firstButtonImage
|
||||
onClicked: function() {
|
||||
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
|
||||
root.firstButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: secondButton
|
||||
visible: root.secondButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.secondButtonImage
|
||||
onClicked: function() {
|
||||
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
|
||||
root.secondButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: resetButton
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
visible: root.textAreaText !== ""
|
||||
image: "qrc:/images/controls/close.svg"
|
||||
|
||||
onClicked: function() {
|
||||
root.textAreaText = ""
|
||||
textArea.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,233 +1,220 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string headerTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property alias errorText: errorField.text
|
||||
property bool checkEmptyText: false
|
||||
property bool rightButtonClickedOnEnter: false
|
||||
|
||||
property string buttonText
|
||||
property string buttonImageSource
|
||||
property var clickedFunc
|
||||
|
||||
property alias textField: textField
|
||||
property string textFieldTextColor: AmneziaStyle.color.paleGray
|
||||
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property bool textFieldEditable: true
|
||||
|
||||
property string borderColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string backgroundColor: AmneziaStyle.color.onyxBlack
|
||||
property string backgroundDisabledColor: AmneziaStyle.color.transparent
|
||||
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
Connections {
|
||||
target: textField
|
||||
function onFocusChanged() {
|
||||
if (textField.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: backgroud
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: input.implicitHeight
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
radius: 16
|
||||
border.color: getBackgroundBorderColor(root.borderColor)
|
||||
border.width: 1
|
||||
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors.fill: backgroud
|
||||
ColumnLayout {
|
||||
Layout.margins: 16
|
||||
LabelTextType {
|
||||
text: root.headerText
|
||||
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
enabled: root.textFieldEditable
|
||||
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
|
||||
|
||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
|
||||
placeholderTextColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: 400
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
height: 24
|
||||
Layout.fillWidth: true
|
||||
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
root.errorText = ""
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.checkEmptyText && text === "") {
|
||||
root.errorText = qsTr("The field can't be empty")
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: contextMenu.open()
|
||||
enabled: true
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textField
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmallTextType {
|
||||
id: errorField
|
||||
|
||||
text: root.errorText
|
||||
visible: root.errorText !== ""
|
||||
color: AmneziaStyle.color.vibrantRed
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: function(mouse) {
|
||||
textField.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
|
||||
}
|
||||
|
||||
|
||||
onExited: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
text: root.buttonText
|
||||
leftImageSource: root.buttonImageSource
|
||||
|
||||
anchors.top: content.top
|
||||
anchors.bottom: content.bottom
|
||||
anchors.right: content.right
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
squareLeftSide: true
|
||||
|
||||
clickedFunc: function() {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundBorderColor(noneFocusedColor) {
|
||||
return textField.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string headerTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property alias errorText: errorField.text
|
||||
property bool checkEmptyText: false
|
||||
property bool rightButtonClickedOnEnter: false
|
||||
|
||||
property string buttonText
|
||||
property string buttonImageSource
|
||||
property var clickedFunc
|
||||
|
||||
property alias textField: textField
|
||||
property string textFieldTextColor: AmneziaStyle.color.paleGray
|
||||
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property bool textFieldEditable: true
|
||||
|
||||
property string borderColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string backgroundColor: AmneziaStyle.color.onyxBlack
|
||||
property string backgroundDisabledColor: AmneziaStyle.color.transparent
|
||||
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: backgroud
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: input.implicitHeight
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
radius: 16
|
||||
border.color: getBackgroundBorderColor(root.borderColor)
|
||||
border.width: 1
|
||||
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors.fill: backgroud
|
||||
ColumnLayout {
|
||||
Layout.margins: 16
|
||||
LabelTextType {
|
||||
text: root.headerText
|
||||
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
enabled: root.textFieldEditable
|
||||
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
|
||||
|
||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
|
||||
placeholderTextColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: 400
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
height: 24
|
||||
Layout.fillWidth: true
|
||||
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
root.errorText = ""
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.checkEmptyText && text === "") {
|
||||
root.errorText = qsTr("The field can't be empty")
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: contextMenu.open()
|
||||
enabled: true
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textField
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmallTextType {
|
||||
id: errorField
|
||||
|
||||
text: root.errorText
|
||||
visible: root.errorText !== ""
|
||||
color: AmneziaStyle.color.vibrantRed
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: function(mouse) {
|
||||
textField.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
|
||||
}
|
||||
|
||||
|
||||
onExited: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
text: root.buttonText
|
||||
leftImageSource: root.buttonImageSource
|
||||
|
||||
anchors.top: content.top
|
||||
anchors.bottom: content.bottom
|
||||
anchors.right: content.right
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
squareLeftSide: true
|
||||
|
||||
clickedFunc: function() {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundBorderColor(noneFocusedColor) {
|
||||
return textField.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ PageType {
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: proxyServersModel
|
||||
|
||||
sourceModel: ServersModel
|
||||
|
||||
filters: [
|
||||
ValueFilter {
|
||||
roleName: "isCurrentlyProcessed"
|
||||
@@ -29,67 +31,55 @@ PageType {
|
||||
]
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
spacing: 16
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: proxyServersModel
|
||||
|
||||
spacing: 16
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
Repeater {
|
||||
model: proxyServersModel
|
||||
delegate: Item {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
headerText: qsTr("Removing services from %1").arg(name)
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Removing services from %1").arg(name)
|
||||
}
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
text: qsTr("Usually it takes no more than 5 minutes")
|
||||
}
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Usually it takes no more than 5 minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,23 +25,17 @@ PageType {
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
@@ -50,16 +44,14 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
model: 1
|
||||
clip: true
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
spacing: 16
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
@@ -87,8 +79,6 @@ PageType {
|
||||
width: listView.width
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -101,8 +101,8 @@ PageType {
|
||||
visible: isLoggingEnabled ? true : false
|
||||
text: qsTr("Logging enabled")
|
||||
|
||||
Keys.onEnterPressed: loggingButton.clicked()
|
||||
Keys.onReturnPressed: loggingButton.clicked()
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
|
||||
onClicked: {
|
||||
PageController.goToPage(PageEnum.PageSettingsLogging)
|
||||
@@ -147,8 +147,8 @@ PageType {
|
||||
leftImageColor: ""
|
||||
rightImageSource: "qrc:/images/controls/chevron-down.svg"
|
||||
|
||||
Keys.onEnterPressed: splitTunnelingButton.clicked()
|
||||
Keys.onReturnPressed: splitTunnelingButton.clicked()
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
|
||||
onClicked: {
|
||||
homeSplitTunnelingDrawer.openTriggered()
|
||||
@@ -276,8 +276,8 @@ PageType {
|
||||
topPadding: 4
|
||||
bottomPadding: 3
|
||||
|
||||
Keys.onEnterPressed: collapsedButtonChevron.clicked()
|
||||
Keys.onReturnPressed: collapsedButtonChevron.clicked()
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
|
||||
onClicked: {
|
||||
if (drawer.isCollapsedStateActive()) {
|
||||
@@ -320,6 +320,9 @@ PageType {
|
||||
|
||||
rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : ""
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
|
||||
onClicked: {
|
||||
ServersModel.processedIndex = ServersModel.defaultIndex
|
||||
|
||||
|
||||
@@ -16,349 +16,397 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.bottom: saveButton.top
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
width: parent.width
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: AwgConfigModel
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === ""
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
spacing: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
KeyNavigation.tab: junkPacketCountTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
headerText: "Jc - Junk packet count"
|
||||
textField.text: clientJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketCount) {
|
||||
clientJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: junkPacketMinSizeTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
headerText: "Jmin - Junk packet minimum size"
|
||||
textField.text: clientJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMinSize) {
|
||||
clientJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
headerText: "Jmax - Junk packet maximum size"
|
||||
textField.text: clientJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMaxSize) {
|
||||
clientJunkPacketMaxSize = textField.text
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk1TextField
|
||||
headerText: qsTr("I1 - First special junk packet")
|
||||
textField.text: clientSpecialJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk1) {
|
||||
clientSpecialJunk1 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jc - Junk packet count"
|
||||
textField.text: clientJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketCount) {
|
||||
clientJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk2TextField
|
||||
headerText: qsTr("I2 - Second special junk packet")
|
||||
textField.text: clientSpecialJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk2) {
|
||||
clientSpecialJunk2 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jmin - Junk packet minimum size"
|
||||
textField.text: clientJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMinSize) {
|
||||
clientJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk3TextField
|
||||
headerText: qsTr("I3 - Third special junk packet")
|
||||
textField.text: clientSpecialJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk3) {
|
||||
clientSpecialJunk3 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jmax - Junk packet maximum size"
|
||||
textField.text: clientJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMaxSize) {
|
||||
clientJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk4TextField
|
||||
headerText: qsTr("I4 - Fourth special junk packet")
|
||||
textField.text: clientSpecialJunk4
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk1TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk4) {
|
||||
clientSpecialJunk4 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I1 - First special junk packet")
|
||||
textField.text: clientSpecialJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk1) {
|
||||
clientSpecialJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk5TextField
|
||||
headerText: qsTr("I5 - Fifth special junk packet")
|
||||
textField.text: clientSpecialJunk5
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk2TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk5 ) {
|
||||
clientSpecialJunk5 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I2 - Second special junk packet")
|
||||
textField.text: clientSpecialJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk2) {
|
||||
clientSpecialJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk1TextField
|
||||
headerText: qsTr("J1 - First controlled junk packet")
|
||||
textField.text: clientControlledJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk3TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk1) {
|
||||
clientControlledJunk1 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I3 - Third special junk packet")
|
||||
textField.text: clientSpecialJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk3) {
|
||||
clientSpecialJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk2TextField
|
||||
headerText: qsTr("J2 - Second controlled junk packet")
|
||||
textField.text: clientControlledJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk4TextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk2) {
|
||||
clientControlledJunk2 = textField.text
|
||||
}
|
||||
headerText: qsTr("I4 - Fourth special junk packet")
|
||||
textField.text: clientSpecialJunk4
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk4) {
|
||||
clientSpecialJunk4 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk3TextField
|
||||
headerText: qsTr("J3 - Third controlled junk packet")
|
||||
textField.text: clientControlledJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk5TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk3) {
|
||||
clientControlledJunk3 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I5 - Fifth special junk packet")
|
||||
textField.text: clientSpecialJunk5
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk5 ) {
|
||||
clientSpecialJunk5 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: iTimeTextField
|
||||
headerText: qsTr("Itime - Special handshake timeout")
|
||||
textField.text: clientSpecialHandshakeTimeout
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: controlledJunk1TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialHandshakeTimeout) {
|
||||
clientSpecialHandshakeTimeout = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J1 - First controlled junk packet")
|
||||
textField.text: clientControlledJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk1) {
|
||||
clientControlledJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
AwgTextField {
|
||||
id: controlledJunk2TextField
|
||||
|
||||
text: qsTr("Server settings")
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J2 - Second controlled junk packet")
|
||||
textField.text: clientControlledJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk2) {
|
||||
clientControlledJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: portTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: controlledJunk3TextField
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J3 - Third controlled junk packet")
|
||||
textField.text: clientControlledJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk3) {
|
||||
clientControlledJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: iTimeTextField
|
||||
|
||||
headerText: "S1 - Init packet junk size"
|
||||
textField.text: serverInitPacketJunkSize
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Itime - Special handshake timeout")
|
||||
textField.text: clientSpecialHandshakeTimeout
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialHandshakeTimeout) {
|
||||
clientSpecialHandshakeTimeout = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
enabled: false
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "S2 - Response packet junk size"
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
}
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
// enabled: false
|
||||
AwgTextField {
|
||||
id: portTextField
|
||||
|
||||
// headerText: "S3 - Cookie Reply packet junk size"
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
// }
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
// enabled: false
|
||||
enabled: false
|
||||
|
||||
// headerText: "S4 - Transport packet junk size"
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
// }
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
|
||||
headerText: "H1 - Init packet magic header"
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
enabled: false
|
||||
enabled: false
|
||||
|
||||
headerText: "H2 - Response packet magic header"
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
}
|
||||
headerText: "S1 - Init packet junk size"
|
||||
textField.text: serverInitPacketJunkSize
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
|
||||
headerText: "H3 - Underload packet magic header"
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
enabled: false
|
||||
|
||||
headerText: "H4 - Transport packet magic header"
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
}
|
||||
headerText: "S2 - Response packet junk size"
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// enabled: false
|
||||
|
||||
// headerText: "S3 - Cookie Reply packet junk size"
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// enabled: false
|
||||
|
||||
// headerText: "S4 - Transport packet junk size"
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H1 - Init packet magic header"
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H2 - Response packet magic header"
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H3 - Underload packet magic header"
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H4 - Transport packet magic header"
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,18 +423,17 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
enabled: listview.currentItem.isSaveButtonEnabled
|
||||
enabled: listView.currentItem.isSaveButtonEnabled
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listview.positionViewAtEnd()
|
||||
listView.positionViewAtEnd()
|
||||
}
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
@@ -401,11 +448,9 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,334 +19,343 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
|
||||
property bool isFocusable: true
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
width: parent.width
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
clip: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
model: AwgConfigModel
|
||||
|
||||
delegate: Item {
|
||||
delegate: ColumnLayout {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
|
||||
width: listView.width
|
||||
|
||||
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
spacing: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
headerText: qsTr("Jc - Junk packet count")
|
||||
textField.text: serverJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketCount) {
|
||||
serverJunkPacketCount = textField.text
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
headerText: qsTr("Jmin - Junk packet minimum size")
|
||||
textField.text: serverJunkPacketMinSize
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMinSize) {
|
||||
serverJunkPacketMinSize = textField.text
|
||||
}
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
headerText: qsTr("Jmax - Junk packet maximum size")
|
||||
textField.text: serverJunkPacketMaxSize
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMaxSize) {
|
||||
serverJunkPacketMaxSize = textField.text
|
||||
}
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jc - Junk packet count")
|
||||
textField.text: serverJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketCount) {
|
||||
serverJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jmin - Junk packet minimum size")
|
||||
textField.text: serverJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMinSize) {
|
||||
serverJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jmax - Junk packet maximum size")
|
||||
textField.text: serverJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMaxSize) {
|
||||
serverJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("S1 - Init packet junk size")
|
||||
textField.text: serverInitPacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketJunkSize) {
|
||||
serverInitPacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("S2 - Response packet junk size")
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketJunkSize) {
|
||||
serverResponsePacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// headerText: qsTr("S3 - Cookie reply packet junk size")
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverCookieReplyPacketJunkSize) {
|
||||
// serverCookieReplyPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// headerText: qsTr("S4 - Transport packet junk size")
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverTransportPacketJunkSize) {
|
||||
// serverTransportPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H1 - Init packet magic header")
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketMagicHeader) {
|
||||
serverInitPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H2 - Response packet magic header")
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketMagicHeader) {
|
||||
serverResponsePacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H3 - Underload packet magic header")
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverUnderloadPacketMagicHeader) {
|
||||
serverUnderloadPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H4 - Transport packet magic header")
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverTransportPacketMagicHeader) {
|
||||
serverTransportPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveRestartButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
|
||||
transportPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketMagicHeaderTextField.errorText === "" &&
|
||||
initPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketJunkSizeTextField.errorText === "" &&
|
||||
// cookieReplyHeaderJunkTextField.errorText === "" &&
|
||||
// transportHeaderJunkTextField.errorText === "" &&
|
||||
initPacketJunkSizeTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === "" &&
|
||||
portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listView.positionViewAtEnd()
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
headerText: qsTr("S1 - Init packet junk size")
|
||||
textField.text: serverInitPacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketJunkSize) {
|
||||
serverInitPacketJunkSize = textField.text
|
||||
clickedFunc: function() {
|
||||
if (delegateItem.isEnabled) {
|
||||
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
|
||||
transportPacketMagicHeaderTextField.textField.text,
|
||||
responsePacketMagicHeaderTextField.textField.text,
|
||||
initPacketMagicHeaderTextField.textField.text)) {
|
||||
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
headerText: qsTr("S2 - Response packet junk size")
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketJunkSize) {
|
||||
serverResponsePacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
// headerText: qsTr("S3 - Cookie reply packet junk size")
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverCookieReplyPacketJunkSize) {
|
||||
// serverCookieReplyPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
// headerText: qsTr("S4 - Transport packet junk size")
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverTransportPacketJunkSize) {
|
||||
// serverTransportPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
headerText: qsTr("H1 - Init packet magic header")
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketMagicHeader) {
|
||||
serverInitPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
headerText: qsTr("H2 - Response packet magic header")
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketMagicHeader) {
|
||||
serverResponsePacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
headerText: qsTr("H3 - Underload packet magic header")
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverUnderloadPacketMagicHeader) {
|
||||
serverUnderloadPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
headerText: qsTr("H4 - Transport packet magic header")
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverTransportPacketMagicHeader) {
|
||||
serverTransportPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BasicButtonType {
|
||||
id: saveRestartButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
|
||||
transportPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketMagicHeaderTextField.errorText === "" &&
|
||||
initPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketJunkSizeTextField.errorText === "" &&
|
||||
// cookieReplyHeaderJunkTextField.errorText === "" &&
|
||||
// transportHeaderJunkTextField.errorText === "" &&
|
||||
initPacketJunkSizeTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === "" &&
|
||||
portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listview.positionViewAtEnd()
|
||||
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
parseInt(responsePacketJunkSizeTextField.textField.text))) {
|
||||
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
|
||||
return
|
||||
}
|
||||
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(responsePacketJunkSizeTextField.textField.text),
|
||||
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(transportPacketJunkSizeTextField.textField.text))) {
|
||||
// PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)"))
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
if (delegateItem.isEnabled) {
|
||||
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
|
||||
transportPacketMagicHeaderTextField.textField.text,
|
||||
responsePacketMagicHeaderTextField.textField.text,
|
||||
initPacketMagicHeaderTextField.textField.text)) {
|
||||
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
|
||||
return
|
||||
}
|
||||
|
||||
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
parseInt(responsePacketJunkSizeTextField.textField.text))) {
|
||||
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
|
||||
return
|
||||
}
|
||||
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(responsePacketJunkSizeTextField.textField.text),
|
||||
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(transportPacketJunkSizeTextField.textField.text))) {
|
||||
// PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)"))
|
||||
// return
|
||||
// }
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveRestartButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,212 +16,192 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Column {
|
||||
id: content
|
||||
property int selectedIndex: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property int selectedIndex: 0
|
||||
headerText: qsTr("Cloak settings")
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
model: CloakConfigModel
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
model: CloakConfigModel
|
||||
property alias trafficFromField: trafficFromField
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
spacing: 0
|
||||
|
||||
property alias trafficFromField: trafficFromField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
TextFieldWithHeaderType {
|
||||
id: trafficFromField
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Cloak settings")
|
||||
var indexHttps = tmpText.indexOf("https://")
|
||||
if (indexHttps === 0) {
|
||||
tmpText = textField.text.substring(8)
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: trafficFromField
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
var indexHttps = tmpText.indexOf("https://")
|
||||
if (indexHttps === 0) {
|
||||
tmpText = textField.text.substring(8)
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
selectedIndex = i
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
selectedIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: trafficFromField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(CloakConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: trafficFromField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(CloakConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,428 +17,486 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
headerText: qsTr("OpenVPN settings")
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
model: ListModel {
|
||||
ListElement { type: "subnetHeader" }
|
||||
ListElement { type: "networkProtocolText" }
|
||||
ListElement { type: "protoSelector" }
|
||||
ListElement { type: "portTextField" }
|
||||
ListElement { type: "encryptionSection" }
|
||||
ListElement { type: "checkboxSection" }
|
||||
ListElement { type: "clientCommands" }
|
||||
ListElement { type: "serverCommands" }
|
||||
}
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
delegate: DelegateChooser {
|
||||
|
||||
model: OpenVpnConfigModel
|
||||
role: "type"
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
DelegateChoice {
|
||||
// property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
|
||||
roleValue: "subnetHeader"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: OpenVpnConfigModel.subnetAddress
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("OpenVPN settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== OpenVpnConfigModel.subnetAddress) {
|
||||
OpenVpnConfigModel.subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
DelegateChoice {
|
||||
roleValue: "networkProtocolText"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Network protocol")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "protoSelector"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
rootWidth: root.width
|
||||
|
||||
enabled: OpenVpnConfigModel.isTransportProtoEditable
|
||||
|
||||
currentIndex: {
|
||||
return OpenVpnConfigModel.transportProto === "tcp" ? 1 : 0
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
text: qsTr("Network protocol")
|
||||
onCurrentIndexChanged: {
|
||||
if (OpenVpnConfigModel.transportProto === "tcp" && currentIndex === 0) {
|
||||
OpenVpnConfigModel.transportProto = "udp"
|
||||
} else if (OpenVpnConfigModel.transportProto === "udp" && currentIndex === 1) {
|
||||
OpenVpnConfigModel.transportProto = "tcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "portTextField"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: OpenVpnConfigModel.isPortEditable
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: OpenVpnConfigModel.port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== OpenVpnConfigModel.port) {
|
||||
OpenVpnConfigModel.port = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "encryptionSection"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
SwitcherType {
|
||||
id: autoNegotiateEncryprionSwitcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Auto-negotiate encryption")
|
||||
checked: OpenVpnConfigModel.autoNegotiateEncryption
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== OpenVpnConfigModel.autoNegotiateEncryprion) {
|
||||
OpenVpnConfigModel.autoNegotiateEncryprion = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: hashDropDown
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
descriptionText: qsTr("Hash")
|
||||
headerText: qsTr("Hash")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: hashListView
|
||||
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
rootWidth: root.width
|
||||
|
||||
enabled: isTransportProtoEditable
|
||||
|
||||
currentIndex: {
|
||||
return transportProto === "tcp" ? 1 : 0
|
||||
model: ListModel {
|
||||
ListElement { name : qsTr("SHA512") }
|
||||
ListElement { name : qsTr("SHA384") }
|
||||
ListElement { name : qsTr("SHA256") }
|
||||
ListElement { name : qsTr("SHA3-512") }
|
||||
ListElement { name : qsTr("SHA3-384") }
|
||||
ListElement { name : qsTr("SHA3-256") }
|
||||
ListElement { name : qsTr("whirlpool") }
|
||||
ListElement { name : qsTr("BLAKE2b512") }
|
||||
ListElement { name : qsTr("BLAKE2s256") }
|
||||
ListElement { name : qsTr("SHA1") }
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (transportProto === "tcp" && currentIndex === 0) {
|
||||
transportProto = "udp"
|
||||
} else if (transportProto === "udp" && currentIndex === 1) {
|
||||
transportProto = "tcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
parentFlickable: fl
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
clickedFunction: function() {
|
||||
hashDropDown.text = selectedText
|
||||
OpenVpnConfigModel.hash = hashDropDown.text
|
||||
hashDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
Component.onCompleted: {
|
||||
hashDropDown.text = OpenVpnConfigModel.hash
|
||||
|
||||
SwitcherType {
|
||||
id: autoNegotiateEncryprionSwitcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Auto-negotiate encryption")
|
||||
checked: autoNegotiateEncryprion
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== autoNegotiateEncryprion) {
|
||||
autoNegotiateEncryprion = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: hashDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
descriptionText: qsTr("Hash")
|
||||
headerText: qsTr("Hash")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: hashListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : qsTr("SHA512") }
|
||||
ListElement { name : qsTr("SHA384") }
|
||||
ListElement { name : qsTr("SHA256") }
|
||||
ListElement { name : qsTr("SHA3-512") }
|
||||
ListElement { name : qsTr("SHA3-384") }
|
||||
ListElement { name : qsTr("SHA3-256") }
|
||||
ListElement { name : qsTr("whirlpool") }
|
||||
ListElement { name : qsTr("BLAKE2b512") }
|
||||
ListElement { name : qsTr("BLAKE2s256") }
|
||||
ListElement { name : qsTr("SHA1") }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
hashDropDown.text = selectedText
|
||||
hash = hashDropDown.text
|
||||
hashDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
hashDropDown.text = hash
|
||||
|
||||
for (var i = 0; i < hashListView.model.count; i++) {
|
||||
if (hashListView.model.get(i).name === hashDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
for (var i = 0; i < hashListView.model.count; i++) {
|
||||
if (hashListView.model.get(i).name === hashDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
drawerParent: root
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
drawerParent: root
|
||||
|
||||
rootWidth: root.width
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : qsTr("AES-256-GCM") }
|
||||
ListElement { name : qsTr("AES-192-GCM") }
|
||||
ListElement { name : qsTr("AES-128-GCM") }
|
||||
ListElement { name : qsTr("AES-256-CBC") }
|
||||
ListElement { name : qsTr("AES-192-CBC") }
|
||||
ListElement { name : qsTr("AES-128-CBC") }
|
||||
ListElement { name : qsTr("ChaCha20-Poly1305") }
|
||||
ListElement { name : qsTr("ARIA-256-CBC") }
|
||||
ListElement { name : qsTr("CAMELLIA-256-CBC") }
|
||||
ListElement { name : qsTr("none") }
|
||||
}
|
||||
rootWidth: root.width
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
model: ListModel {
|
||||
ListElement { name : qsTr("AES-256-GCM") }
|
||||
ListElement { name : qsTr("AES-192-GCM") }
|
||||
ListElement { name : qsTr("AES-128-GCM") }
|
||||
ListElement { name : qsTr("AES-256-CBC") }
|
||||
ListElement { name : qsTr("AES-192-CBC") }
|
||||
ListElement { name : qsTr("AES-128-CBC") }
|
||||
ListElement { name : qsTr("ChaCha20-Poly1305") }
|
||||
ListElement { name : qsTr("ARIA-256-CBC") }
|
||||
ListElement { name : qsTr("CAMELLIA-256-CBC") }
|
||||
ListElement { name : qsTr("none") }
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
OpenVpnConfigModel.cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = OpenVpnConfigModel.cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.preferredHeight: checkboxLayout.implicitHeight
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
radius: 16
|
||||
DelegateChoice {
|
||||
roleValue: "checkboxSection"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
Connections {
|
||||
target: tlsAuthCheckBox
|
||||
enabled: !GC.isMobile()
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
|
||||
function onFocusChanged() {
|
||||
if (tlsAuthCheckBox.activeFocus) {
|
||||
fl.ensureVisible(contentRect)
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.preferredHeight: checkboxLayout.implicitHeight
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
radius: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: checkboxLayout
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
CheckBoxType {
|
||||
id: tlsAuthCheckBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("TLS auth")
|
||||
checked: OpenVpnConfigModel.tlsAuth
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== OpenVpnConfigModel.tlsAuth) {
|
||||
console.log("tlsAuth changed to: " + checked)
|
||||
OpenVpnConfigModel.tlsAuth = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: checkboxLayout
|
||||
DividerType {}
|
||||
|
||||
anchors.fill: parent
|
||||
CheckBoxType {
|
||||
id: tlsAuthCheckBox
|
||||
Layout.fillWidth: true
|
||||
CheckBoxType {
|
||||
id: blockDnsCheckBox
|
||||
|
||||
text: qsTr("TLS auth")
|
||||
checked: tlsAuth
|
||||
Layout.fillWidth: true
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== tlsAuth) {
|
||||
console.log("tlsAuth changed to: " + checked)
|
||||
tlsAuth = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
text: qsTr("Block DNS requests outside of VPN")
|
||||
checked: OpenVpnConfigModel.blockDns
|
||||
|
||||
DividerType {}
|
||||
|
||||
CheckBoxType {
|
||||
id: blockDnsCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Block DNS requests outside of VPN")
|
||||
checked: blockDns
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== blockDns) {
|
||||
blockDns = checked
|
||||
}
|
||||
onCheckedChanged: {
|
||||
if (checked !== OpenVpnConfigModel.blockDns) {
|
||||
OpenVpnConfigModel.blockDns = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: additionalClientCommandsSwitcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
parentFlickable: fl
|
||||
DelegateChoice {
|
||||
roleValue: "clientCommands"
|
||||
ColumnLayout {
|
||||
|
||||
checked: additionalClientCommands !== ""
|
||||
width: listView.width
|
||||
|
||||
text: qsTr("Additional client configuration commands")
|
||||
SwitcherType {
|
||||
id: additionalClientCommandsSwitcher
|
||||
|
||||
onCheckedChanged: {
|
||||
if (!checked) {
|
||||
additionalClientCommands = ""
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
checked: OpenVpnConfigModel.additionalClientCommands !== ""
|
||||
|
||||
text: qsTr("Additional client configuration commands")
|
||||
|
||||
onCheckedChanged: {
|
||||
if (!checked) {
|
||||
OpenVpnConfigModel.additionalClientCommands = ""
|
||||
}
|
||||
// listView.positionViewAtIndex(index, ListView.Beginning)
|
||||
}
|
||||
}
|
||||
|
||||
TextAreaType {
|
||||
id: additionalClientCommandsTextArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: additionalClientCommandsSwitcher.checked
|
||||
|
||||
textAreaText: OpenVpnConfigModel.additionalClientCommands
|
||||
placeholderText: qsTr("Commands:")
|
||||
|
||||
textArea.onEditingFinished: {
|
||||
if (OpenVpnConfigModel.additionalClientCommands !== textAreaText) {
|
||||
OpenVpnConfigModel.additionalClientCommands = textAreaText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextAreaType {
|
||||
id: additionalClientCommandsTextArea
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
DelegateChoice {
|
||||
roleValue: "serverCommands"
|
||||
ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
visible: additionalClientCommandsSwitcher.checked
|
||||
SwitcherType {
|
||||
id: additionalServerCommandsSwitcher
|
||||
|
||||
parentFlickable: fl
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textAreaText: additionalClientCommands
|
||||
placeholderText: qsTr("Commands:")
|
||||
checked: OpenVpnConfigModel.additionalServerCommands !== ""
|
||||
|
||||
textArea.onEditingFinished: {
|
||||
if (additionalClientCommands !== textAreaText) {
|
||||
additionalClientCommands = textAreaText
|
||||
}
|
||||
text: qsTr("Additional server configuration commands")
|
||||
|
||||
onCheckedChanged: {
|
||||
if (!checked) {
|
||||
OpenVpnConfigModel.additionalServerCommands = ""
|
||||
}
|
||||
// listView.positionViewAtIndex(index, ListView.Beginning)
|
||||
}
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: additionalServerCommandsSwitcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
TextAreaType {
|
||||
id: additionalServerCommandsTextArea
|
||||
|
||||
checked: additionalServerCommands !== ""
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Additional server configuration commands")
|
||||
visible: additionalServerCommandsSwitcher.checked
|
||||
|
||||
onCheckedChanged: {
|
||||
if (!checked) {
|
||||
additionalServerCommands = ""
|
||||
}
|
||||
textAreaText: OpenVpnConfigModel.additionalServerCommands
|
||||
placeholderText: qsTr("Commands:")
|
||||
|
||||
textArea.onEditingFinished: {
|
||||
if (OpenVpnConfigModel.additionalServerCommands !== textAreaText) {
|
||||
OpenVpnConfigModel.additionalServerCommands = textAreaText
|
||||
}
|
||||
}
|
||||
|
||||
TextAreaType {
|
||||
id: additionalServerCommandsTextArea
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: additionalServerCommandsSwitcher.checked
|
||||
|
||||
textAreaText: additionalServerCommands
|
||||
placeholderText: qsTr("Commands:")
|
||||
parentFlickable: fl
|
||||
textArea.onEditingFinished: {
|
||||
if (additionalServerCommands !== textAreaText) {
|
||||
additionalServerCommands = textAreaText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: vpnAddressSubnetTextField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
parentFlickable: fl
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: saveRestartButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: vpnAddressSubnetTextField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,164 +19,154 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: header.bottom
|
||||
anchors.left: parent.left
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
contentHeight: content.height
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 32
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: parent.width
|
||||
height: contentItem.height
|
||||
clip: true
|
||||
interactive: false
|
||||
model: ProtocolsModel
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
}
|
||||
}
|
||||
|
||||
activeFocusOnTab: true
|
||||
focus: true
|
||||
model: ProtocolsModel
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias focusItem: button
|
||||
LabelWithButtonType {
|
||||
id: button
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
anchors.fill: parent
|
||||
text: qsTr("Show connection options")
|
||||
|
||||
LabelWithButtonType {
|
||||
id: button
|
||||
clickedFunction: function() {
|
||||
configContentDrawer.openTriggered()
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
MouseArea {
|
||||
anchors.fill: button
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Show connection options")
|
||||
DividerType {}
|
||||
|
||||
clickedFunction: function() {
|
||||
configContentDrawer.openTriggered()
|
||||
}
|
||||
DrawerType2 {
|
||||
id: configContentDrawer
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: button
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: configContentDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: drawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
configContentDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: drawerListView
|
||||
|
||||
anchors.top: drawerBackButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Connection options %1").arg(protocolName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
DrawerType2 {
|
||||
id: configContentDrawer
|
||||
delegate: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
expandedHeight: root.height * 0.9
|
||||
TextArea {
|
||||
id: configText
|
||||
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: configContentDrawer.expandedHeight
|
||||
padding: 0
|
||||
height: 24
|
||||
|
||||
BackButtonType {
|
||||
id: backButton1
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
backButtonFunction: function() {
|
||||
configContentDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
text: rawConfig
|
||||
|
||||
FlickableType {
|
||||
anchors.top: backButton1.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
ColumnLayout {
|
||||
id: configContent
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
headerText: qsTr("Connection options %1").arg(protocolName)
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: configText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
padding: 0
|
||||
leftPadding: 0
|
||||
height: 24
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
text: rawConfig
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
@@ -198,11 +188,7 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
@@ -16,179 +16,158 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: ShadowSocksConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
spacing: 0
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
}
|
||||
|
||||
model: ShadowSocksConfigModel
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
enabled: listView.enabled
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
}
|
||||
enabled: listView.enabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
drawerParent: root
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
listView: ListViewWithRadioButtonType {
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
id: cipherListView
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,160 +16,124 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
Item {
|
||||
id: focusItem
|
||||
onFocusChanged: {
|
||||
if (activeFocus) {
|
||||
fl.ensureVisible(focusItem)
|
||||
}
|
||||
}
|
||||
KeyNavigation.tab: backButton
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
model: WireGuardConfigModel
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
spacing: 0
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
model: WireGuardConfigModel
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
KeyNavigation.tab: saveButton
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
anchors.right: root.right
|
||||
anchors.left: root.left
|
||||
anchors.bottom: root.bottom
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
anchors.topMargin: 24
|
||||
anchors.bottomMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
enabled: listview.currentItem.isSaveButtonEnabled
|
||||
enabled: listView.currentItem.isSaveButtonEnabled
|
||||
|
||||
text: qsTr("Save")
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,153 +16,134 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: WireGuardConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
spacing: 0
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
model: WireGuardConfigModel
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
property alias focusItemId: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,163 +17,141 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
|
||||
Column {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
model: XrayConfigModel
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
|
||||
property alias focusItemId: textFieldWithHeaderType.textField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: textFieldWithHeaderType
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
if (tmpText.startsWith("https://")) {
|
||||
tmpText = textField.text.substring(8)
|
||||
site = tmpText
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(XrayConfigModel.getConfig())
|
||||
//focusItem.forceActiveFocus()
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
model: XrayConfigModel
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias focusItemId: textFieldWithHeaderType.textField
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: textFieldWithHeaderType
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: listView.enabled
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
if (tmpText.startsWith("https://")) {
|
||||
tmpText = textField.text.substring(8)
|
||||
site = tmpText
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: listView.enabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(XrayConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,50 +16,53 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
headerText: "AmneziaDNS"
|
||||
descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") +
|
||||
qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
|
||||
Layout.topMargin: 24
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
@@ -71,19 +74,14 @@ PageType {
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
|
||||
&& SettingsController.isAmneziaDnsEnabled()) {
|
||||
&& SettingsController.isAmneziaDnsEnabled()) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
removeButton.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
@@ -24,258 +24,215 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: SftpConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
spacing: 0
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
headerText: qsTr("SFTP settings")
|
||||
}
|
||||
|
||||
model: SftpConfigModel
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
listview.currentItem.listViewFocusItem.forceActiveFocus()
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: mountButton
|
||||
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Mount folder on device")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.mountSftpDrive(port, password, username)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
|
||||
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
|
||||
|
||||
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
|
||||
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
|
||||
if (Qt.platform.os === "windows") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "osx") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "linux") {
|
||||
return ""
|
||||
} else return ""
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
property alias listViewFocusItem: hostLabel.rightButton
|
||||
BasicButtonType {
|
||||
id: detailedInstructionsButton
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 8
|
||||
implicitHeight: 32
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
spacing: 0
|
||||
text: qsTr("Detailed instructions")
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("SFTP settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: mountButton
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Mount folder on device")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.mountSftpDrive(port, password, username)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
|
||||
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
|
||||
|
||||
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
|
||||
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
|
||||
if (Qt.platform.os === "windows") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "osx") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "linux") {
|
||||
return ""
|
||||
} else return ""
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: detailedInstructionsButton
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 8
|
||||
implicitHeight: 32
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("Detailed instructions")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunc: function() {
|
||||
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||
}
|
||||
}
|
||||
}
|
||||
clickedFunc: function() {
|
||||
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,327 +25,290 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: listview.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
model: Socks5ProxyConfigModel
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
spacing: 0
|
||||
|
||||
model: Socks5ProxyConfigModel
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
listview.currentItem.focusItemId.forceActiveFocus()
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: content.implicitHeight
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
property alias focusItemId: hostLabel.rightButton
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: changeSettingsDrawer
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
property string tempPort: port
|
||||
property string tempUsername: username
|
||||
property string tempPassword: password
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 32
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
Connections {
|
||||
target: changeSettingsDrawer
|
||||
function onOpened() {
|
||||
tempPort = port
|
||||
tempUsername = username
|
||||
tempPassword = password
|
||||
}
|
||||
function onClosed() {
|
||||
port = tempPort
|
||||
username = tempUsername
|
||||
password = tempPassword
|
||||
portTextField.textField.text = port
|
||||
usernameTextField.textField.text = username
|
||||
passwordTextField.textField.text = password
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.topMargin: 40
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
parentFlickable: fl
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: usernameTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
headerText: qsTr("Username")
|
||||
textField.placeholderText: "username"
|
||||
textField.text: username
|
||||
textField.maximumLength: 32
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== username) {
|
||||
username = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
property bool hidePassword: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
headerText: qsTr("Password")
|
||||
textField.placeholderText: "password"
|
||||
textField.text: password
|
||||
textField.maximumLength: 32
|
||||
|
||||
descriptionOnTop: true
|
||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
||||
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
||||
: ""
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
clickedFunc: function() {
|
||||
hidePassword = !hidePassword
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: changeSettingsDrawer
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
property string tempPort: port
|
||||
property string tempUsername: username
|
||||
property string tempPassword: password
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 32
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
Connections {
|
||||
target: changeSettingsDrawer
|
||||
function onOpened() {
|
||||
tempPort = port
|
||||
tempUsername = username
|
||||
tempPassword = password
|
||||
}
|
||||
function onClosed() {
|
||||
port = tempPort
|
||||
username = tempUsername
|
||||
password = tempPassword
|
||||
portTextField.textField.text = port
|
||||
usernameTextField.textField.text = username
|
||||
passwordTextField.textField.text = password
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: usernameTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Username")
|
||||
textField.placeholderText: "username"
|
||||
textField.text: username
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== username) {
|
||||
username = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
property bool hidePassword: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Password")
|
||||
textField.placeholderText: "password"
|
||||
textField.text: password
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
||||
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
||||
: ""
|
||||
|
||||
clickedFunc: function() {
|
||||
hidePassword = !hidePassword
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== password) {
|
||||
password = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
if (!portTextField.textField.acceptableInput) {
|
||||
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
|
||||
passwordTextField.errorText = qsTr("Password cannot be empty")
|
||||
return
|
||||
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
|
||||
usernameTextField.errorText = qsTr("Username cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
|
||||
tempPort = portTextField.textField.text
|
||||
tempUsername = usernameTextField.textField.text
|
||||
tempPassword = passwordTextField.textField.text
|
||||
changeSettingsDrawer.closeTriggered()
|
||||
}
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== password) {
|
||||
password = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changeSettingsButton
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
changeSettingsDrawer.openTriggered()
|
||||
if (!portTextField.textField.acceptableInput) {
|
||||
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
|
||||
passwordTextField.errorText = qsTr("Password cannot be empty")
|
||||
return
|
||||
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
|
||||
usernameTextField.errorText = qsTr("Username cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
|
||||
tempPort = portTextField.textField.text
|
||||
tempUsername = usernameTextField.textField.text
|
||||
tempPassword = passwordTextField.textField.text
|
||||
changeSettingsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changeSettingsButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
changeSettingsDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,34 +25,31 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
@@ -61,11 +58,19 @@ PageType {
|
||||
|
||||
headerText: qsTr("Tor website settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser after migrate to 6.9
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: websiteName
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Website address")
|
||||
descriptionText: {
|
||||
@@ -83,15 +88,16 @@ PageType {
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
|
||||
@@ -1,171 +1,167 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: account
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Servers")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/server.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsServersList)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: connection
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Connection")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/radio.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsConnection)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: application
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Application")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/app.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsApplication)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: news
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("News & Notifications")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: NewsModel.hasUnread ? "qrc:/images/controls/news-unread.svg" : "qrc:/images/controls/news.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsNewsNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: backup
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Backup")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/save.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsBackup)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: about
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("About AmneziaVPN")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/amnezia.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAbout)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: devConsole
|
||||
visible: SettingsController.isDevModeEnabled
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Dev console")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/bug.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageDevMenu)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: SettingsController.isDevModeEnabled
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: close
|
||||
visible: GC.isDesktop()
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: about.height
|
||||
|
||||
text: qsTr("Close application")
|
||||
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
||||
isLeftImageHoverEnabled: false
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.closeApplication()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: GC.isDesktop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: settingsEntries
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: isVisible
|
||||
|
||||
text: title
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: leftImagePath
|
||||
|
||||
clickedFunction: clickedHandler
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: close
|
||||
|
||||
visible: GC.isDesktop()
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Close application")
|
||||
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
||||
isLeftImageHoverEnabled: false
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.closeApplication()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: GC.isDesktop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> settingsEntries: [
|
||||
servers,
|
||||
connection,
|
||||
application,
|
||||
backup,
|
||||
about,
|
||||
devConsole
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: servers
|
||||
|
||||
property string title: qsTr("Servers")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsServersList)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: connection
|
||||
|
||||
property string title: qsTr("Connection")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/radio.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsConnection)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: application
|
||||
|
||||
property string title: qsTr("Application")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/app.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsApplication)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: backup
|
||||
|
||||
property string title: qsTr("Backup")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/save.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsBackup)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: about
|
||||
|
||||
property string title: qsTr("About AmneziaVPN")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAbout)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: devConsole
|
||||
|
||||
property string title: qsTr("Dev console")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/bug.svg"
|
||||
property bool isVisible: SettingsController.isDevModeEnabled
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageDevMenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,58 +29,7 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: telegramGroup
|
||||
|
||||
readonly property string title: qsTr("Telegram group")
|
||||
readonly property string description: qsTr("To discuss features")
|
||||
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: mail
|
||||
|
||||
readonly property string title: qsTr("support@amnezia.org")
|
||||
readonly property string description: qsTr("For reviews and bug reports")
|
||||
readonly property string imageSource: "qrc:/images/controls/mail.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: github
|
||||
|
||||
readonly property string title: qsTr("GitHub")
|
||||
readonly property string description: qsTr("Discover the source code")
|
||||
readonly property string imageSource: "qrc:/images/controls/github.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: website
|
||||
|
||||
readonly property string title: qsTr("Website")
|
||||
readonly property string description: qsTr("Visit official website")
|
||||
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> contacts: [
|
||||
telegramGroup,
|
||||
mail,
|
||||
github,
|
||||
website
|
||||
]
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
@@ -88,38 +37,6 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
model: contacts
|
||||
|
||||
clip: true
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
@@ -170,11 +87,12 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
model: contacts
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: telegramButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
|
||||
@@ -257,4 +175,55 @@ PageType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> contacts: [
|
||||
telegramGroup,
|
||||
mail,
|
||||
github,
|
||||
website
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: telegramGroup
|
||||
|
||||
readonly property string title: qsTr("Telegram group")
|
||||
readonly property string description: qsTr("To discuss features")
|
||||
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: mail
|
||||
|
||||
readonly property string title: qsTr("support@amnezia.org")
|
||||
readonly property string description: qsTr("For reviews and bug reports")
|
||||
readonly property string imageSource: "qrc:/images/controls/mail.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: github
|
||||
|
||||
readonly property string title: qsTr("GitHub")
|
||||
readonly property string description: qsTr("Discover the source code")
|
||||
readonly property string imageSource: "qrc:/images/controls/github.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: website
|
||||
|
||||
readonly property string title: qsTr("Website")
|
||||
readonly property string description: qsTr("Visit official website")
|
||||
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
|
||||
readonly property var handler: function() {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ PageType {
|
||||
checkable: !ConnectionController.isConnected
|
||||
|
||||
onClicked: {
|
||||
if (ConnectionController.isConnectionInProgress) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change server location while trying to make an active connection"))
|
||||
return
|
||||
}
|
||||
if (ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection"))
|
||||
return
|
||||
|
||||
@@ -20,49 +20,49 @@ PageType {
|
||||
id: windows
|
||||
|
||||
readonly property string title: qsTr("Windows")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#windows")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: macos
|
||||
|
||||
readonly property string title: qsTr("macOS")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#macos")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: android
|
||||
|
||||
readonly property string title: qsTr("Android")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#android")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: androidTv
|
||||
|
||||
readonly property string title: qsTr("AndroidTV")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/")
|
||||
readonly property string link: qsTr("documentation/instructions/android_tv_connect/")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: ios
|
||||
|
||||
readonly property string title: qsTr("iOS")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#ios")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: linux
|
||||
|
||||
readonly property string title: qsTr("Linux")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#linux")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: routers
|
||||
|
||||
readonly property string title: qsTr("Routers")
|
||||
readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers")
|
||||
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#routers")
|
||||
}
|
||||
|
||||
property list<QtObject> instructionsModel: [
|
||||
@@ -114,7 +114,7 @@ PageType {
|
||||
rightImageSource: "qrc:/images/controls/external-link.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
Qt.openUrlExternally(link)
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,220 +1,242 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property string configExtension: ".conf"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 20
|
||||
anchors.bottomMargin: 24
|
||||
|
||||
model: ApiCountryModel
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Configuration Files")
|
||||
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
|
||||
text: countryName
|
||||
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isIssued) {
|
||||
moreOptionsDrawer.countryName = countryName
|
||||
moreOptionsDrawer.countryCode = countryCode
|
||||
moreOptionsDrawer.openTriggered()
|
||||
} else {
|
||||
issueConfig(countryCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: moreOptionsDrawer
|
||||
|
||||
property string countryName
|
||||
property string countryCode
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: moreOptionsDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: moreOptionsDrawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.top: moreOptionsDrawerBackButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
contentHeight: moreOptionsDrawerContent.height
|
||||
|
||||
ColumnLayout {
|
||||
id: moreOptionsDrawerContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Generate a new configuration file")
|
||||
descriptionText: qsTr("The previously created one will stop working")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Revoke the current configuration file")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function issueConfig(countryCode) {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = countryCode + configExtension
|
||||
} else {
|
||||
fileName = SystemController.getFileName(configCaption,
|
||||
qsTr("Config files (*" + configExtension + ")"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
|
||||
true,
|
||||
configExtension)
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("Config file saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function revokeConfig(countryCode) {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.revokeNativeConfig(countryCode)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("The config has been revoked"))
|
||||
}
|
||||
}
|
||||
|
||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||
var headerText
|
||||
if (isConfigIssue) {
|
||||
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||
} else {
|
||||
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||
}
|
||||
|
||||
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (isConfigIssue) {
|
||||
issueConfig(countryCode)
|
||||
} else {
|
||||
revokeConfig(countryCode)
|
||||
}
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import QtCore
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property string configExtension: ".conf"
|
||||
property string configCaption: qsTr("Save AmneziaVPN config")
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
model: ApiCountryModel
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Configuration Files")
|
||||
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
|
||||
text: countryName
|
||||
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
|
||||
descriptionColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
|
||||
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isIssued) {
|
||||
moreOptionsDrawer.countryName = countryName
|
||||
moreOptionsDrawer.countryCode = countryCode
|
||||
moreOptionsDrawer.openTriggered()
|
||||
} else {
|
||||
issueConfig(countryCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: moreOptionsDrawer
|
||||
|
||||
property string countryName
|
||||
property string countryCode
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.4375
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: moreOptionsDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: moreOptionsDrawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: drawerListView
|
||||
|
||||
anchors.top: moreOptionsDrawerBackButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
header: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Generate a new configuration file")
|
||||
descriptionText: qsTr("The previously created one will stop working")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Revoke the current configuration file")
|
||||
|
||||
clickedFunction: function() {
|
||||
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function issueConfig(countryCode) {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
fileName = countryCode + configExtension
|
||||
} else {
|
||||
fileName = SystemController.getFileName(configCaption,
|
||||
qsTr("Config files (*" + configExtension + ")"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
|
||||
true,
|
||||
configExtension)
|
||||
}
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("Config file saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function revokeConfig(countryCode) {
|
||||
PageController.showBusyIndicator(true)
|
||||
let result = ApiConfigsController.revokeNativeConfig(countryCode)
|
||||
if (result) {
|
||||
ApiSettingsController.getAccountInfo(true)
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (result) {
|
||||
PageController.showNotificationMessage(qsTr("The config has been revoked"))
|
||||
}
|
||||
}
|
||||
|
||||
function showQuestion(isConfigIssue, countryCode, countryName) {
|
||||
var headerText
|
||||
if (isConfigIssue) {
|
||||
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
|
||||
} else {
|
||||
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
|
||||
}
|
||||
|
||||
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
|
||||
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (isConfigIssue) {
|
||||
issueConfig(countryCode)
|
||||
} else {
|
||||
revokeConfig(countryCode)
|
||||
}
|
||||
moreOptionsDrawer.closeTriggered()
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,17 +111,6 @@ PageType {
|
||||
serverNameEditDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
RenameServerDrawer {
|
||||
id: serverNameEditDrawer
|
||||
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.35
|
||||
|
||||
serverNameText: root.processedServer.name
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
@@ -207,35 +196,30 @@ PageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: warning.visible ? 16 : 32
|
||||
|
||||
visible: false //footer.isVisibleForAmneziaFree
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
|
||||
text: qsTr("Subscription Key")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
shareConnectionDrawer.headerText = qsTr("Amnezia Premium subscription key")
|
||||
|
||||
shareConnectionDrawer.openTriggered()
|
||||
shareConnectionDrawer.isSelfHostedConfig = false;
|
||||
shareConnectionDrawer.shareButtonText = qsTr("Save VPN key as a file")
|
||||
shareConnectionDrawer.copyButtonText = qsTr("Copy VPN key")
|
||||
|
||||
|
||||
PageController.goToPage(PageEnum.PageSettingsApiSubscriptionKey)
|
||||
PageController.showBusyIndicator(true)
|
||||
|
||||
ApiConfigsController.prepareVpnKeyExport()
|
||||
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
// Navigate to PageShareConnection page
|
||||
//PageController.goToPage(PageEnum.PageShareConnection)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: false //footer.isVisibleForAmneziaFree
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: warning.visible ? 16 : 32
|
||||
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
|
||||
@@ -420,9 +404,12 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ShareConnectionDrawer {
|
||||
id: shareConnectionDrawer
|
||||
RenameServerDrawer {
|
||||
id: serverNameEditDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.35
|
||||
|
||||
serverNameText: root.processedServer.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import QtCore
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.prepareVpnKeyExport()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.fill: parent
|
||||
contentHeight: layout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
width: root.width
|
||||
|
||||
BackButtonType {
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
text: qsTr("Amnezia Premium\nsubscription key")
|
||||
font.pixelSize: 32
|
||||
font.bold: true
|
||||
color: AmneziaStyle.color.paleGray
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.paleGray
|
||||
hoveredColor: AmneziaStyle.color.sheerWhite
|
||||
pressedColor: AmneziaStyle.color.translucentWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.black
|
||||
leftImageColor: "black"
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Copy key")
|
||||
leftImageSource: "qrc:/images/controls/copy.svg"
|
||||
|
||||
onClicked: {
|
||||
ApiConfigsController.copyVpnKeyToClipboard()
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: "transparent"
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Save key as a file")
|
||||
leftImageSource: "qrc:/images/controls/share-2.svg"
|
||||
|
||||
onClicked: {
|
||||
var fileName = GC.isMobile()
|
||||
? "amnezia_vpn_key.vpn"
|
||||
: SystemController.getFileName(
|
||||
qsTr("Save AmneziaVPN config"),
|
||||
qsTr("Config files (*.vpn)"),
|
||||
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_vpn_key",
|
||||
true,
|
||||
".vpn"
|
||||
)
|
||||
|
||||
if (fileName !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
ExportController.exportConfig(fileName)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
defaultColor: "transparent"
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Show key text")
|
||||
leftImageSource: "qrc:/images/controls/eye.svg"
|
||||
|
||||
onClicked: {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.prepareVpnKeyExport()
|
||||
PageController.showBusyIndicator(false)
|
||||
vpnKeyDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: ApiConfigsController.qrCodesCount > 0
|
||||
color: "white"
|
||||
radius: 12
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
smooth: false
|
||||
source: ApiConfigsController.qrCodesCount > 0 && ApiConfigsController.qrCodes[0] ? ApiConfigsController.qrCodes[0] : ""
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
visible: ApiConfigsController.qrCodesCount > 0
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("To read the QR code in the Amnezia app, tap + in the main menu → 'QR code'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: vpnKeyDrawer
|
||||
|
||||
anchors.fill: root
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
expandedStateContent: Item {
|
||||
BackButtonType {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 16
|
||||
backButtonFunction: function() { vpnKeyDrawer.closeTriggered() }
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 56
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Amnezia Premium Subscription key")
|
||||
}
|
||||
|
||||
TextArea {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
readOnly: true
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
text: ApiConfigsController.vpnKey //|| ""
|
||||
wrapMode: Text.Wrap
|
||||
background: Rectangle { color: AmneziaStyle.color.transparent }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ PageType {
|
||||
readonly property string name: qsTr("Only the apps from the list should have access via VPN")
|
||||
readonly property int type: routeMode.onlyForwardApps
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: allExceptApps
|
||||
|
||||
@@ -111,7 +112,7 @@ PageType {
|
||||
|
||||
headerText: qsTr("Mode")
|
||||
|
||||
enabled: Qt.platform.os === "android" && root.pageEnabled
|
||||
enabled: (Qt.platform.os === "android") && root.pageEnabled
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
@@ -146,77 +147,56 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
|
||||
anchors.bottom: addAppButton.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: root.pageEnabled
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyAppSplitTunnelingModel
|
||||
sourceModel: AppSplitTunnelingModel
|
||||
filters: RegExpFilter {
|
||||
roleName: "appPath"
|
||||
pattern: ".*" + searchField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
sorters: [
|
||||
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
|
||||
]
|
||||
}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: apps
|
||||
width: parent.width
|
||||
height: apps.contentItem.height
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyAppSplitTunnelingModel
|
||||
sourceModel: AppSplitTunnelingModel
|
||||
filters: RegExpFilter {
|
||||
roleName: "appPath"
|
||||
pattern: ".*" + searchField.textField.text + ".*"
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: appPath
|
||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Remove ") + appPath + "?"
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
|
||||
}
|
||||
sorters: [
|
||||
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
|
||||
]
|
||||
}
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: apps.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: appPath
|
||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Remove ") + appPath + "?"
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,22 +21,24 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
@@ -45,9 +47,17 @@ PageType {
|
||||
|
||||
headerText: qsTr("Application")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
|
||||
|
||||
width: listView.width
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
id: switcherAllowScreenshots
|
||||
|
||||
visible: GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -61,10 +71,6 @@ PageType {
|
||||
SettingsController.toggleScreenshotsEnabled(checked)
|
||||
}
|
||||
}
|
||||
|
||||
// KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
|
||||
// labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
|
||||
parentFlickable: fl
|
||||
}
|
||||
|
||||
DividerType {
|
||||
@@ -73,15 +79,15 @@ PageType {
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonNotification
|
||||
|
||||
visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Enable notifications")
|
||||
descriptionText: qsTr("Enable notifications to show the VPN state in the status bar")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
SettingsController.requestNotificationPermission()
|
||||
}
|
||||
@@ -93,6 +99,7 @@ PageType {
|
||||
|
||||
SwitcherType {
|
||||
id: switcherAutoStart
|
||||
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -101,8 +108,6 @@ PageType {
|
||||
text: qsTr("Auto start")
|
||||
descriptionText: qsTr("Launch the application every time the device is starts")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
checked: SettingsController.isAutoStartEnabled()
|
||||
onCheckedChanged: {
|
||||
if (checked !== SettingsController.isAutoStartEnabled()) {
|
||||
@@ -117,6 +122,7 @@ PageType {
|
||||
|
||||
SwitcherType {
|
||||
id: switcherAutoConnect
|
||||
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -125,8 +131,6 @@ PageType {
|
||||
text: qsTr("Auto connect")
|
||||
descriptionText: qsTr("Connect to VPN on app start")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
checked: SettingsController.isAutoConnectEnabled()
|
||||
onCheckedChanged: {
|
||||
if (checked !== SettingsController.isAutoConnectEnabled()) {
|
||||
@@ -141,15 +145,17 @@ PageType {
|
||||
|
||||
SwitcherType {
|
||||
id: switcherStartMinimized
|
||||
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("Start minimized")
|
||||
descriptionText: qsTr("Launch application minimized")
|
||||
descriptionText: qsTr("Launch application minimized (works with autostart option turned on)")
|
||||
|
||||
parentFlickable: fl
|
||||
enabled: switcherAutoStart.checked
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
|
||||
checked: SettingsController.isStartMinimizedEnabled()
|
||||
onCheckedChanged: {
|
||||
@@ -162,17 +168,21 @@ PageType {
|
||||
DividerType {
|
||||
visible: !GC.isMobile()
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonLanguage
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Language")
|
||||
descriptionText: LanguageModel.currentLanguageName
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
selectLanguageDrawer.openTriggered()
|
||||
}
|
||||
@@ -182,14 +192,13 @@ PageType {
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonLogging
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Logging")
|
||||
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsLogging)
|
||||
}
|
||||
@@ -199,14 +208,13 @@ PageType {
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonReset
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Reset settings and remove all data from the application")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Reset settings and remove all data from the application?")
|
||||
var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
|
||||
|
||||
@@ -41,51 +41,68 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
header: ColumnLayout {
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
width: listView.width
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Back up your configuration")
|
||||
descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
|
||||
|
||||
width: listView.width
|
||||
|
||||
spacing: 16
|
||||
|
||||
WarningType {
|
||||
Layout.topMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textString: qsTr("The backup will contain your passwords and private keys for all servers added " +
|
||||
"to AmneziaVPN. Keep this information in a secure place.")
|
||||
"to AmneziaVPN. Keep this information in a secure place.")
|
||||
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: makeBackupButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 14
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Make a backup")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunc: function() {
|
||||
var fileName = ""
|
||||
if (GC.isMobile()) {
|
||||
@@ -108,8 +125,11 @@ PageType {
|
||||
|
||||
BasicButtonType {
|
||||
id: restoreBackupButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
@@ -120,8 +140,6 @@ PageType {
|
||||
|
||||
text: qsTr("Restore from backup")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunc: function() {
|
||||
var filePath = SystemController.getFileName(qsTr("Open backup file"),
|
||||
qsTr("Backup files (*.backup)"))
|
||||
|
||||
@@ -21,20 +21,25 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
header: ColumnLayout {
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
@@ -43,9 +48,17 @@ PageType {
|
||||
|
||||
headerText: qsTr("Connection")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser when have migrated to 6.9
|
||||
|
||||
width: listView.width
|
||||
|
||||
SwitcherType {
|
||||
id: amneziaDnsSwitch
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
@@ -64,14 +77,13 @@ PageType {
|
||||
|
||||
LabelWithButtonType {
|
||||
id: dnsServersButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("DNS servers")
|
||||
descriptionText: qsTr("When AmneziaDNS is not used or installed")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsDns)
|
||||
}
|
||||
@@ -81,14 +93,13 @@ PageType {
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
descriptionText: qsTr("Allows you to select which sites you want to access through the VPN")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
}
|
||||
@@ -96,8 +107,15 @@ PageType {
|
||||
|
||||
DividerType {}
|
||||
|
||||
}
|
||||
|
||||
footer: ColumnLayout { // TODO(CyAn84): move to delegate,add DelegateChooser when have migrated to 6.9
|
||||
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingButton2
|
||||
|
||||
visible: root.isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -106,8 +124,6 @@ PageType {
|
||||
descriptionText: qsTr("Allows you to use the VPN only for certain Apps")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
}
|
||||
@@ -127,8 +143,6 @@ PageType {
|
||||
descriptionText: qsTr("Blocks network connections without VPN")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsKillSwitch)
|
||||
}
|
||||
|
||||
@@ -1,140 +1,167 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
|
||||
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
|
||||
|
||||
enabled: !isServerFromApi
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isServerFromApi) {
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("DNS servers")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("If AmneziaDNS is not used or installed")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: primaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Primary DNS")
|
||||
|
||||
textField.text: SettingsController.primaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: secondaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Secondary DNS")
|
||||
|
||||
textField.text: SettingsController.secondaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: restoreDefaultButton
|
||||
Layout.fillWidth: true
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Restore default")
|
||||
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Restore default DNS settings?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
SettingsController.primaryDns = "1.1.1.1"
|
||||
primaryDns.textField.text = SettingsController.primaryDns
|
||||
SettingsController.secondaryDns = "1.0.0.1"
|
||||
secondaryDns.textField.text = SettingsController.secondaryDns
|
||||
PageController.showNotificationMessage(qsTr("Settings have been reset"))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (primaryDns.textField.text !== SettingsController.primaryDns) {
|
||||
SettingsController.primaryDns = primaryDns.textField.text
|
||||
}
|
||||
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
|
||||
SettingsController.secondaryDns = secondaryDns.textField.text
|
||||
}
|
||||
PageController.showNotificationMessage(qsTr("Settings saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
|
||||
|
||||
enabled: !isServerFromApi
|
||||
|
||||
Component.onCompleted: {
|
||||
if (isServerFromApi) {
|
||||
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
|
||||
}
|
||||
}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("DNS servers")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("If AmneziaDNS is not used or installed")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: primaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Primary DNS")
|
||||
|
||||
textField.text: SettingsController.primaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: secondaryDns
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Secondary DNS")
|
||||
|
||||
textField.text: SettingsController.secondaryDns
|
||||
textField.validator: RegularExpressionValidator {
|
||||
regularExpression: InstallController.ipAddressRegExp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
spacing: 16
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: restoreDefaultButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Restore default")
|
||||
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Restore default DNS settings?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
SettingsController.primaryDns = "1.1.1.1"
|
||||
primaryDns.textField.text = SettingsController.primaryDns
|
||||
SettingsController.secondaryDns = "1.0.0.1"
|
||||
secondaryDns.textField.text = SettingsController.secondaryDns
|
||||
PageController.showNotificationMessage(qsTr("Settings have been reset"))
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (primaryDns.textField.text !== SettingsController.primaryDns) {
|
||||
SettingsController.primaryDns = primaryDns.textField.text
|
||||
}
|
||||
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
|
||||
SettingsController.secondaryDns = secondaryDns.textField.text
|
||||
}
|
||||
PageController.showNotificationMessage(qsTr("Settings saved"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ PageType {
|
||||
onClicked: function() {
|
||||
SettingsController.strictKillSwitchEnabled = false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
@@ -103,6 +106,9 @@ PageType {
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
|
||||
DividerType {
|
||||
|
||||
@@ -64,7 +64,7 @@ PageType {
|
||||
displayMarginBeginning: 40
|
||||
displayMarginEnd: 40
|
||||
|
||||
ScrollBar.vertical: ScrollBarType { }
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
footer: Item {
|
||||
width: listView.width
|
||||
|
||||
@@ -23,9 +23,15 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
@@ -33,10 +39,6 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
@@ -101,8 +103,7 @@ PageType {
|
||||
}
|
||||
|
||||
model: logTypes
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
snapMode: ListView.SnapOneItem
|
||||
|
||||
delegate: ColumnLayout {
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
property var newsItem
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: proxyNews
|
||||
sourceModel: NewsModel
|
||||
filters: [ ValueFilter { roleName: "isProcessed"; value: true } ]
|
||||
Component.onCompleted: root.newsItem = proxyNews.get(0)
|
||||
}
|
||||
Connections {
|
||||
target: NewsModel
|
||||
function onProcessedIndexChanged() {
|
||||
root.newsItem = proxyNews.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
headerText: newsItem.title
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: newsItem.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("News & Notifications")
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: newsList
|
||||
width: parent.width
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
model: NewsModel
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: newsList.width
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
leftImageSource: read ? "" : "qrc:/images/controls/unread-dot.svg"
|
||||
isSmallLeftImage: !read
|
||||
text: title
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
NewsModel.markAsRead(index)
|
||||
NewsModel.processedIndex = index
|
||||
PageController.goToPage(PageEnum.PageSettingsNewsDetail)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,6 @@ PageType {
|
||||
|
||||
signal lastItemTabClickedSignal()
|
||||
|
||||
onFocusChanged: content.isServerWithWriteAccess ?
|
||||
labelWithButton.forceActiveFocus() :
|
||||
labelWithButton3.forceActiveFocus()
|
||||
|
||||
Connections {
|
||||
target: InstallController
|
||||
|
||||
@@ -63,218 +59,194 @@ PageType {
|
||||
target: ServersModel
|
||||
|
||||
function onProcessedServerIndexChanged() {
|
||||
content.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
|
||||
listView.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.fill: parent
|
||||
|
||||
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
|
||||
model: serverActions
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton
|
||||
visible: content.isServerWithWriteAccess
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Check the server for previously installed Amnezia services")
|
||||
descriptionText: qsTr("Add them to the application if they were not displayed")
|
||||
visible: isVisible
|
||||
|
||||
text: title
|
||||
descriptionText: description
|
||||
textColor: tColor
|
||||
|
||||
clickedFunction: function() {
|
||||
clickedHandler()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> serverActions: [
|
||||
check,
|
||||
reboot,
|
||||
remove,
|
||||
clear,
|
||||
reset,
|
||||
switch_to_premium,
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: check
|
||||
|
||||
property bool isVisible: true
|
||||
readonly property string title: qsTr("Check the server for previously installed Amnezia services")
|
||||
readonly property string description: qsTr("Add them to the application if they were not displayed")
|
||||
readonly property var tColor: AmneziaStyle.color.paleGray
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.scanServerForInstalledContainers()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: reboot
|
||||
|
||||
property bool isVisible: true
|
||||
readonly property string title: qsTr("Reboot server")
|
||||
readonly property string description: ""
|
||||
readonly property var tColor: AmneziaStyle.color.vibrantRed
|
||||
readonly property var clickedHandler: function() {
|
||||
var headerText = qsTr("Do you want to reboot the server?")
|
||||
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.scanServerForInstalledContainers()
|
||||
InstallController.rebootProcessedServer()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
DividerType {
|
||||
visible: content.isServerWithWriteAccess
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton2
|
||||
visible: content.isServerWithWriteAccess
|
||||
Layout.fillWidth: true
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Reboot server")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
QtObject {
|
||||
id: remove
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Do you want to reboot the server?")
|
||||
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
property bool isVisible: true
|
||||
readonly property string title: qsTr("Remove server from application")
|
||||
readonly property string description: ""
|
||||
readonly property var tColor: AmneziaStyle.color.vibrantRed
|
||||
readonly property var clickedHandler: function() {
|
||||
var headerText = qsTr("Do you want to remove the server from application?")
|
||||
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.rebootProcessedServer()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton5.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton2.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.removeProcessedServer()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
DividerType {
|
||||
visible: content.isServerWithWriteAccess
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton3
|
||||
Layout.fillWidth: true
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Remove server from application")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
QtObject {
|
||||
id: clear
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Do you want to remove the server from application?")
|
||||
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
property bool isVisible: true
|
||||
readonly property string title: qsTr("Clear server from Amnezia software")
|
||||
readonly property string description: ""
|
||||
readonly property var tColor: AmneziaStyle.color.vibrantRed
|
||||
readonly property var clickedHandler: function() {
|
||||
var headerText = qsTr("Do you want to clear server from Amnezia software?")
|
||||
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.removeProcessedServer()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton5.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton3.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeAllContainers()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton4
|
||||
visible: content.isServerWithWriteAccess
|
||||
Layout.fillWidth: true
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Clear server from Amnezia software")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
QtObject {
|
||||
id: reset
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Do you want to clear server from Amnezia software?")
|
||||
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||
readonly property string title: qsTr("Reset API config")
|
||||
readonly property string description: ""
|
||||
readonly property var tColor: AmneziaStyle.color.vibrantRed
|
||||
readonly property var clickedHandler: function() {
|
||||
var headerText = qsTr("Do you want to reset API config?")
|
||||
var descriptionText = ""
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeAllContainers()
|
||||
}
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton5.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton4.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.removeApiConfig(ServersModel.processedIndex)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
DividerType {
|
||||
visible: content.isServerWithWriteAccess
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton5
|
||||
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||
Layout.fillWidth: true
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Reset API config")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
QtObject {
|
||||
id: switch_to_premium
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Do you want to reset API config?")
|
||||
var descriptionText = ""
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.removeApiConfig(ServersModel.processedIndex)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton5.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
labelWithButton5.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButton6
|
||||
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Switch to the new Amnezia Premium subscription")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPageHome()
|
||||
ApiPremV1MigrationController.showMigrationDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
|
||||
}
|
||||
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
|
||||
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription")
|
||||
readonly property string description: ""
|
||||
readonly property var tColor: AmneziaStyle.color.vibrantRed
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPageHome()
|
||||
ApiPremV1MigrationController.showMigrationDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,249 +21,211 @@ PageType {
|
||||
|
||||
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
}
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ListView {
|
||||
id: protocols
|
||||
Layout.fillWidth: true
|
||||
height: protocols.contentItem.height
|
||||
clip: true
|
||||
interactive: true
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property bool isFocusable: true
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
model: ProtocolsModel
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: protocols.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
|
||||
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
LabelWithButtonType {
|
||||
id: clientSettings
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: protocolName + qsTr(" connection settings")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
visible: delegateContent.isClientSettingsVisible
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isClientProtocolExists) {
|
||||
switch (protocolIndex) {
|
||||
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
PageController.goToPage(clientProtocolPage);
|
||||
} else {
|
||||
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: clientSettings
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: delegateContent.isClientSettingsVisible
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: serverSettings
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: protocolName + qsTr(" server settings")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
visible: delegateContent.isServerSettingsVisible
|
||||
|
||||
clickedFunction: function() {
|
||||
switch (protocolIndex) {
|
||||
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
PageController.goToPage(serverProtocolPage);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: serverSettings
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: delegateContent.isServerSettingsVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: header.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: clearCacheButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: root.isClearCacheVisible
|
||||
|
||||
text: qsTr("Clear profile")
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
|
||||
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
|
||||
PageController.showNotificationMessage(message)
|
||||
return
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.clearCachedProfile()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
// if (!GC.isMobile()) {
|
||||
// focusItem.forceActiveFocus()
|
||||
// }
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: clearCacheButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: root.isClearCacheVisible
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
text: qsTr("Remove ")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
|
||||
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
|
||||
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
|
||||
} else
|
||||
{
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: removeButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
}
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: ProtocolsModel
|
||||
|
||||
delegate: ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
width: listView.width
|
||||
|
||||
property bool isClientSettingsVisible: (protocolIndex === ProtocolEnum.WireGuard) || (protocolIndex === ProtocolEnum.Awg)
|
||||
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
LabelWithButtonType {
|
||||
id: clientSettings
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: protocolName + qsTr(" connection settings")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
visible: delegateContent.isClientSettingsVisible
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isClientProtocolExists) {
|
||||
switch (protocolIndex) {
|
||||
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
PageController.goToPage(clientProtocolPage);
|
||||
} else {
|
||||
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: clientSettings
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: delegateContent.isClientSettingsVisible
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: serverSettings
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: protocolName + qsTr(" server settings")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
visible: delegateContent.isServerSettingsVisible
|
||||
|
||||
clickedFunction: function() {
|
||||
switch (protocolIndex) {
|
||||
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
|
||||
}
|
||||
PageController.goToPage(serverProtocolPage);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: serverSettings
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: delegateContent.isServerSettingsVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: clearCacheButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: root.isClearCacheVisible
|
||||
|
||||
text: qsTr("Clear profile")
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
|
||||
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
|
||||
PageController.showNotificationMessage(message)
|
||||
return
|
||||
}
|
||||
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.clearCachedProfile()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: clearCacheButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: root.isClearCacheVisible
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
text: qsTr("Remove ")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
|
||||
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
|
||||
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
|
||||
} else
|
||||
{
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: removeButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,25 +40,20 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: servers
|
||||
objectName: "servers"
|
||||
|
||||
width: parent.width
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
height: 500
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
model: ServersModel
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: servers.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
|
||||
@@ -161,7 +161,7 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: header.bottom
|
||||
@@ -172,8 +172,6 @@ PageType {
|
||||
|
||||
enabled: root.pageEnabled
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxySitesModel
|
||||
sourceModel: SitesModel
|
||||
@@ -193,13 +191,7 @@ PageType {
|
||||
]
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
reuseItems: true
|
||||
|
||||
delegate: ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
@@ -236,7 +228,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: addSiteButton
|
||||
anchors.bottomMargin: -24
|
||||
@@ -308,7 +299,7 @@ PageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
headerText: qsTr("Import / Export Sites")
|
||||
headerText: qsTr("Additional options")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
@@ -351,6 +342,34 @@ PageType {
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: clearSitesButton
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Clear site list")
|
||||
rightImageSource: "qrc:/images/controls/trash.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
var headerText = qsTr("Clear site list?")
|
||||
var descriptionText = qsTr("All sites will be removed from list.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
SitesController.removeSites()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,22 +393,24 @@ PageType {
|
||||
backButtonFunction: function() {
|
||||
importSitesDrawer.closeTriggered()
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
importSitesDrawerListView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
ListViewType {
|
||||
id: importSitesDrawerListView
|
||||
|
||||
anchors.top: importSitesDrawerBackButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
contentHeight: importSitesDrawerContent.height
|
||||
|
||||
ColumnLayout {
|
||||
id: importSitesDrawerContent
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
header: ColumnLayout {
|
||||
width: importSitesDrawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
@@ -397,49 +418,67 @@ PageType {
|
||||
|
||||
headerText: qsTr("Import a list of sites")
|
||||
}
|
||||
}
|
||||
|
||||
model: importOptions
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: importSitesDrawerListView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: importSitesButton2
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Replace site list")
|
||||
text: title
|
||||
|
||||
clickedFunction: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open sites file"),
|
||||
qsTr("Sites files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
importSitesDrawerContent.importSites(fileName, true)
|
||||
}
|
||||
clickedHandler()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: importSitesButton3
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Add imported sites to existing ones")
|
||||
|
||||
clickedFunction: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open sites file"),
|
||||
qsTr("Sites files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
importSitesDrawerContent.importSites(fileName, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importSites(fileName, replaceExistingSites) {
|
||||
PageController.showBusyIndicator(true)
|
||||
SitesController.importSites(fileName, replaceExistingSites)
|
||||
PageController.showBusyIndicator(false)
|
||||
importSitesDrawer.closeTriggered()
|
||||
moreActionsDrawer.closeTriggered()
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> importOptions: [
|
||||
replaceOption,
|
||||
addOption,
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: replaceOption
|
||||
|
||||
readonly property string title: qsTr("Replace site list")
|
||||
readonly property var clickedHandler: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open sites file"),
|
||||
qsTr("Sites files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
root.importSites(fileName, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: addOption
|
||||
|
||||
readonly property string title: qsTr("Add imported sites to existing ones")
|
||||
readonly property var clickedHandler: function() {
|
||||
var fileName = SystemController.getFileName(qsTr("Open sites file"),
|
||||
qsTr("Sites files (*.json)"))
|
||||
if (fileName !== "") {
|
||||
root.importSites(fileName, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importSites(fileName, replaceExistingSites) {
|
||||
PageController.showBusyIndicator(true)
|
||||
SitesController.importSites(fileName, replaceExistingSites)
|
||||
PageController.showBusyIndicator(false)
|
||||
importSitesDrawer.closeTriggered()
|
||||
moreActionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,177 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: "qrc:/images/controls/map-pin.svg"
|
||||
leftText: qsTr("For the region")
|
||||
rightText: ApiServicesModel.getSelectedServiceData("region")
|
||||
}
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: "qrc:/images/controls/tag.svg"
|
||||
leftText: qsTr("Price")
|
||||
rightText: ApiServicesModel.getSelectedServiceData("price")
|
||||
}
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: "qrc:/images/controls/history.svg"
|
||||
leftText: qsTr("Work period")
|
||||
rightText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
|
||||
visible: rightText !== ""
|
||||
}
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: "qrc:/images/controls/gauge.svg"
|
||||
leftText: qsTr("Speed")
|
||||
rightText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
}
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: "qrc:/images/controls/info.svg"
|
||||
leftText: qsTr("Features")
|
||||
rightText: ""
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl())
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.topMargin: 32
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.bottomMargin: 32
|
||||
|
||||
text: qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
if (endpoint !== undefined && endpoint !== "") {
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.importServiceFromGateway()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: imagePath
|
||||
leftText: lText
|
||||
rightText: rText
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free"))
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
if (endpoint !== undefined && endpoint !== "") {
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
} else {
|
||||
PageController.showBusyIndicator(true)
|
||||
ApiConfigsController.importServiceFromGateway()
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
region,
|
||||
price,
|
||||
timeLimit,
|
||||
speed,
|
||||
features
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: region
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
|
||||
readonly property string lText: qsTr("For the region")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: price
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/tag.svg"
|
||||
readonly property string lText: qsTr("Price")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: timeLimit
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/history.svg"
|
||||
readonly property string lText: qsTr("Work period")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
property bool isVisible: rText !== ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: speed
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
|
||||
readonly property string lText: qsTr("Speed")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: features
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/info.svg"
|
||||
readonly property string lText: qsTr("Features")
|
||||
readonly property string rText: ""
|
||||
property bool isVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,86 +14,77 @@ import "../Config"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
Layout.topMargin: 20
|
||||
|
||||
spacing: 0
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
Layout.topMargin: 20
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("VPN by Amnezia")
|
||||
descriptionText: qsTr("Choose a VPN service that suits your needs.")
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: servicesListView
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.top: backButton.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 16
|
||||
spacing: 0
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
headerText: qsTr("VPN by Amnezia")
|
||||
descriptionText: qsTr("Choose a VPN service that suits your needs.")
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
model: ApiServicesModel
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
delegate: ColumnLayout {
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: servicesListView.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
width: listView.width
|
||||
|
||||
enabled: isServiceAvailable
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
CardWithIconsType {
|
||||
id: card
|
||||
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
CardWithIconsType {
|
||||
id: card
|
||||
headerText: name
|
||||
bodyText: cardDescription
|
||||
footerText: price
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
headerText: name
|
||||
bodyText: cardDescription
|
||||
footerText: price
|
||||
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
onClicked: {
|
||||
if (isServiceAvailable) {
|
||||
ApiServicesModel.setServiceIndex(index)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
||||
}
|
||||
onClicked: {
|
||||
if (isServiceAvailable) {
|
||||
ApiServicesModel.setServiceIndex(index)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,21 +27,11 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
model: variants
|
||||
|
||||
clip: true
|
||||
|
||||
reuseItems: true
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
@@ -216,6 +206,8 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
model: variants
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
@@ -234,6 +226,9 @@ PageType {
|
||||
leftImageSource: imageSource
|
||||
|
||||
onClicked: { handler() }
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,41 +28,14 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
@@ -78,8 +51,6 @@ PageType {
|
||||
|
||||
model: inputFields
|
||||
spacing: 16
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
@@ -177,6 +148,9 @@ PageType {
|
||||
onClicked: {
|
||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide"))
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ PageType {
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: proxyContainersModel
|
||||
|
||||
sourceModel: ContainersModel
|
||||
filters: [
|
||||
ValueFilter {
|
||||
@@ -40,104 +41,98 @@ PageType {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
property int dockerContainer
|
||||
property int containerDefaultPort
|
||||
property int containerDefaultTransportProto
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Column {
|
||||
id: content
|
||||
spacing: 16
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 16
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
implicitWidth: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerTextMaximumLineCount: 10
|
||||
|
||||
headerText: qsTr("Choose Installation Type")
|
||||
}
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
model: proxyContainersModel
|
||||
currentIndex: 0
|
||||
|
||||
ListView {
|
||||
id: containers
|
||||
width: parent.width
|
||||
height: containers.contentItem.height
|
||||
spacing: 16
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
currentIndex: 0
|
||||
clip: true
|
||||
interactive: false
|
||||
model: proxyContainersModel
|
||||
CardType {
|
||||
id: card
|
||||
|
||||
property int dockerContainer
|
||||
property int containerDefaultPort
|
||||
property int containerDefaultTransportProto
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
property bool isFocusable: true
|
||||
headerText: easySetupHeader
|
||||
bodyText: easySetupDescription
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: containers.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
ButtonGroup.group: buttonGroup
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
onClicked: function() {
|
||||
isEasySetup = true
|
||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
CardType {
|
||||
id: card
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: easySetupHeader
|
||||
bodyText: easySetupDescription
|
||||
|
||||
ButtonGroup.group: buttonGroup
|
||||
|
||||
onClicked: function() {
|
||||
isEasySetup = true
|
||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||
|
||||
containers.dockerContainer = dockerContainer
|
||||
containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
|
||||
containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
|
||||
}
|
||||
}
|
||||
}
|
||||
listView.dockerContainer = dockerContainer
|
||||
listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
|
||||
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var item = containers.itemAtIndex(containers.currentIndex)
|
||||
if (item !== null) {
|
||||
var button = item.children[0].children[0]
|
||||
button.checked = true
|
||||
button.clicked()
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 16
|
||||
|
||||
DividerType {
|
||||
implicitWidth: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
}
|
||||
|
||||
CardType {
|
||||
implicitWidth: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Manual")
|
||||
bodyText: qsTr("Choose a VPN protocol")
|
||||
@@ -146,25 +141,29 @@ PageType {
|
||||
|
||||
onClicked: function() {
|
||||
isEasySetup = false
|
||||
checked = true
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
implicitWidth: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Continue")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunc: function() {
|
||||
if (root.isEasySetup) {
|
||||
ContainersModel.setProcessedContainerIndex(containers.dockerContainer)
|
||||
ContainersModel.setProcessedContainerIndex(listView.dockerContainer)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.install(containers.dockerContainer,
|
||||
containers.containerDefaultPort,
|
||||
containers.containerDefaultTransportProto)
|
||||
InstallController.install(listView.dockerContainer,
|
||||
listView.containerDefaultPort,
|
||||
listView.containerDefaultTransportProto)
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardProtocols)
|
||||
}
|
||||
@@ -174,9 +173,11 @@ PageType {
|
||||
BasicButtonType {
|
||||
id: setupLaterButton
|
||||
|
||||
implicitWidth: parent.width
|
||||
anchors.topMargin: 8
|
||||
anchors.bottomMargin: 24
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
@@ -185,9 +186,6 @@ PageType {
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
||||
parentFlickable: fl
|
||||
|
||||
visible: {
|
||||
if (PageController.isTriggeredByConnectButton()) {
|
||||
PageController.setTriggeredByConnectButton(false)
|
||||
@@ -205,5 +203,15 @@ PageType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var item = listView.itemAtIndex(listView.currentIndex)
|
||||
if (item !== null) {
|
||||
var button = item.children[0]
|
||||
button.checked = true
|
||||
button.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,92 +85,76 @@ PageType {
|
||||
]
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
currentIndex: -1
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: proxyContainersModel
|
||||
|
||||
spacing: 16
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: container
|
||||
width: parent.width
|
||||
height: container.contentItem.height
|
||||
currentIndex: -1
|
||||
clip: true
|
||||
interactive: false
|
||||
model: proxyContainersModel
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: container.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
headerText: qsTr("Installing")
|
||||
descriptionText: name
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
headerText: qsTr("Installing")
|
||||
descriptionText: name
|
||||
}
|
||||
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: root.isTimerRunning
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: progressText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
text: root.progressBarText
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: cancelIntallationButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
|
||||
visible: root.isCancelButtonVisible
|
||||
|
||||
text: qsTr("Cancel installation")
|
||||
|
||||
clickedFunc: function() {
|
||||
InstallController.cancelInstallation()
|
||||
PageController.showBusyIndicator(true)
|
||||
}
|
||||
}
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: root.isTimerRunning
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: progressText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: root.progressBarText
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: cancelIntallationButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: root.isCancelButtonVisible
|
||||
|
||||
text: qsTr("Cancel installation")
|
||||
|
||||
clickedFunc: function() {
|
||||
InstallController.cancelInstallation()
|
||||
PageController.showBusyIndicator(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,256 +29,239 @@ PageType {
|
||||
]
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: processedContainerListView
|
||||
width: parent.width
|
||||
height: contentItem.height
|
||||
currentIndex: -1
|
||||
clip: true
|
||||
interactive: false
|
||||
model: proxyContainersModel
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
property bool isFocusable: true
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
currentIndex: -1
|
||||
|
||||
model: proxyContainersModel
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Installing %1").arg(name)
|
||||
descriptionText: description
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: showDetailsButton
|
||||
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
implicitHeight: 32
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("More detailed")
|
||||
|
||||
clickedFunc: function() {
|
||||
showDetailsDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
DrawerType2 {
|
||||
id: showDetailsDrawer
|
||||
parent: root
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: showDetailsDrawer.expandedHeight
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
BackButtonType {
|
||||
id: showDetailsBackButton
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: processedContainerListView.width
|
||||
implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height
|
||||
|
||||
property alias port:port
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
Layout.topMargin: 20
|
||||
Layout.rightMargin: -16
|
||||
Layout.leftMargin: -16
|
||||
backButtonFunction: function() {
|
||||
showDetailsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
ListViewType {
|
||||
id: showDetailsListView
|
||||
|
||||
Layout.fillWidth: true
|
||||
anchors.top: showDetailsBackButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
headerText: qsTr("Installing %1").arg(name)
|
||||
descriptionText: description
|
||||
}
|
||||
header: ColumnLayout {
|
||||
width: showDetailsListView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: showDetailsButton
|
||||
Header2Type {
|
||||
id: showDetailsDrawerHeader
|
||||
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: -8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
implicitHeight: 32
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("More detailed")
|
||||
KeyNavigation.tab: transportProtoSelector
|
||||
|
||||
clickedFunc: function() {
|
||||
showDetailsDrawer.openTriggered()
|
||||
headerText: name
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: showDetailsDrawer
|
||||
parent: root
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: showDetailsDrawer.expandedHeight
|
||||
delegate: ColumnLayout {
|
||||
width: showDetailsListView.width
|
||||
|
||||
BackButtonType {
|
||||
id: showDetailsBackButton
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
text: detailedDescription
|
||||
textFormat: Text.MarkdownText
|
||||
}
|
||||
|
||||
backButtonFunction: function() {
|
||||
showDetailsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: showDetailsBackButton.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: {
|
||||
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
|
||||
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
|
||||
showDetailsDrawerContent.height : emptySpaceHeight
|
||||
}
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: showDetailsDrawerContent
|
||||
footer: ColumnLayout {
|
||||
width: showDetailsListView.width
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
BasicButtonType {
|
||||
id: showDetailsCloseButton
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Header2Type {
|
||||
id: showDetailsDrawerHeader
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
text: qsTr("Close")
|
||||
|
||||
headerText: name
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: detailedDescription
|
||||
textFormat: Text.MarkdownText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: showDetailsCloseButton
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 32
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Close")
|
||||
|
||||
clickedFunc: function() {
|
||||
showDetailsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
clickedFunc: function() {
|
||||
showDetailsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: transportProtoHeader
|
||||
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Network protocol")
|
||||
}
|
||||
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
|
||||
Layout.fillWidth: true
|
||||
rootWidth: root.width
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: port
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: installButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
text: qsTr("Install")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (!port.textField.acceptableInput &&
|
||||
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
|
||||
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
|
||||
port.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||
|
||||
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
|
||||
port.visible = false
|
||||
} else {
|
||||
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
|
||||
}
|
||||
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
|
||||
|
||||
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
|
||||
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
|
||||
transportProtoSelector.visible = protocolSelectorVisible
|
||||
transportProtoHeader.visible = protocolSelectorVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
id: transportProtoHeader
|
||||
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
text: qsTr("Network protocol")
|
||||
}
|
||||
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
rootWidth: root.width
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: port
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: installButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 32
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
text: qsTr("Install")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (!port.textField.acceptableInput &&
|
||||
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
|
||||
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
|
||||
port.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
|
||||
|
||||
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
|
||||
port.visible = false
|
||||
} else {
|
||||
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
|
||||
}
|
||||
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
|
||||
|
||||
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
|
||||
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
|
||||
transportProtoSelector.visible = protocolSelectorVisible
|
||||
transportProtoHeader.visible = protocolSelectorVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user