Compare commits

..

1 Commits

Author SHA1 Message Date
Yaroslav Gurov 20989ea832 feat: integrated xray as a library 2025-07-31 13:13:42 +02:00
110 changed files with 7136 additions and 7438 deletions
+2 -30
View File
@@ -255,20 +255,6 @@ jobs:
env: env:
# Keep compat with MacOS 10.15 aka Catalina by Qt 6.4 # Keep compat with MacOS 10.15 aka Catalina by Qt 6.4
QT_VERSION: 6.4.3 QT_VERSION: 6.4.3
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@@ -309,7 +295,7 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
bash deploy/build_macos.sh -n bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -332,20 +318,6 @@ jobs:
env: env:
QT_VERSION: 6.8.0 QT_VERSION: 6.8.0
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }}
MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }}
MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }}
MAC_INSTALLER_SIGNER_CERT: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }}
MAC_INSTALLER_SIGNER_ID: ${{ secrets.MAC_INSTALLER_SIGNER_ID }}
MAC_INSTALL_CERT_PW: ${{ secrets.MAC_INSTALL_CERT_PW }}
APPLE_DEV_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }}
APPLE_DEV_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }}
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
@@ -386,7 +358,7 @@ jobs:
- name: 'Build project' - name: 'Build project'
run: | run: |
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
bash deploy/build_macos.sh -n bash deploy/build_macos.sh
- name: 'Upload installer artifact' - name: 'Upload installer artifact'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
-1
View File
@@ -138,4 +138,3 @@ CMakeFiles/
ios-ne-build.sh ios-ne-build.sh
macos-ne-build.sh macos-ne-build.sh
macos-signed-build.sh macos-signed-build.sh
macos-with-sign-build.sh
+2 -3
View File
@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.9.2)
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION} project(${PROJECT} VERSION 4.8.8.1
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -12,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2092) set(APP_ANDROID_VERSION_CODE 2087)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+5 -5
View File
@@ -9,17 +9,17 @@
### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md) ### [English]([https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md](https://github.com/amnezia-vpn/amnezia-client/tree/dev?tab=readme-ov-file#)) | [Русский](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README_RU.md)
[Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. [Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting) ### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/amnezia/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting)
> [!TIP] > [!TIP]
> If the [Amnezia website](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-en-mirror). > If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/amnezia/amnezia.org ).
<a href="https://amnezia.org/en/downloads?utm_source=github&utm_campaign=amnezia_button-readme-en"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a> <a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/en/downloads&utm_source=github&utm_campaign=amnezia_button-readme-en-mirrow"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a> <a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases) [All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
+4 -4
View File
@@ -6,16 +6,16 @@
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский ### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере. [AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) [![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org)
### [Сайт](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting) ### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
> [!TIP] > [!TIP]
> Если [сайт Amnezia](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru-mirror). > Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
<a href="https://storage.googleapis.com/amnezia/amnezia.org?m-path=/ru/downloads&utm_source=github&utm_campaign=amnezia_button-readme-ru-mirror"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a> <a href="https://storage.googleapis.com/amnezia/q9p19109"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website-ru.svg" width="150" style="max-width: 100%; margin-right: 10px"></a>
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases) [Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
+2 -4
View File
@@ -10,8 +10,6 @@ import java.nio.channels.FileChannel
import java.nio.channels.FileLock import java.nio.channels.FileLock
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.ZonedDateTime
import java.time.ZoneOffset
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import org.amnezia.vpn.util.Log.Priority.D import org.amnezia.vpn.util.Log.Priority.D
import org.amnezia.vpn.util.Log.Priority.E import org.amnezia.vpn.util.Log.Priority.E
@@ -137,8 +135,8 @@ object Log {
} }
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String { private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
val utcDate = ZonedDateTime.now(ZoneOffset.UTC).format(dateTimeFormat) val date = LocalDateTime.now().format(dateTimeFormat)
return "${utcDate}Z ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " + return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
"$tag: $msg\n" "$tag: $msg\n"
} }
+7
View File
@@ -10,6 +10,7 @@ include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/") set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/") set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
set(LIBXRAY_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libxray")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib") set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
@@ -27,6 +28,9 @@ if(WIN32)
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib") set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif() endif()
elseif(APPLE AND NOT IOS) elseif(APPLE AND NOT IOS)
set(LIBS ${LIBS} "resolv")
set(LIBXRAY_LIB_PATH "${LIBXRAY_ROOT_DIR}/macos/x86_64/libxray.a")
set(LIBXRAY_INCLUDE_DIR "${LIBXRAY_ROOT_DIR}/macos/x86_64")
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a") set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a") set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64") set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
@@ -72,6 +76,8 @@ set(LIBS ${LIBS}
${OPENSSL_LIB_CRYPTO_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
) )
set(LIBS ${LIBS} ${LIBXRAY_LIB_PATH})
add_compile_definitions(_WINSOCKAPI_) add_compile_definitions(_WINSOCKAPI_)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
@@ -88,4 +94,5 @@ include_directories(
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain ${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain ${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include ${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
${LIBXRAY_INCLUDE_DIR}
) )
+1 -5
View File
@@ -18,11 +18,7 @@ set(LIBS ${LIBS}
${FW_NETWORK_EXTENSION} ${FW_NETWORK_EXTENSION}
) )
set_target_properties(${PROJECT} PROPERTIES set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
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_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
+4 -8
View File
@@ -120,9 +120,6 @@ void CoreController::initControllers()
connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(), connect(m_installController.get(), &InstallController::currentContainerUpdated, m_connectionController.get(),
&ConnectionController::onCurrentContainerUpdated); // TODO remove this &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_importController.reset(new ImportController(m_serversModel, m_containersModel, m_settings));
m_engine->rootContext()->setContextProperty("ImportController", m_importController.get()); m_engine->rootContext()->setContextProperty("ImportController", m_importController.get());
@@ -245,10 +242,7 @@ void CoreController::initNotificationHandler()
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(), connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
&ConnectionController::closeConnection); &ConnectionController::closeConnection);
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated); 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);
#endif
} }
void CoreController::updateTranslator(const QLocale &locale) void CoreController::updateTranslator(const QLocale &locale)
@@ -285,7 +279,6 @@ void CoreController::updateTranslator(const QLocale &locale)
m_engine->retranslate(); m_engine->retranslate();
emit translationsUpdated(); emit translationsUpdated();
emit websiteUrlChanged(m_languageModel->getCurrentSiteUrl());
} }
void CoreController::initErrorMessagesHandler() void CoreController::initErrorMessagesHandler()
@@ -306,10 +299,13 @@ void CoreController::setQmlRoot()
void CoreController::initApiCountryModelUpdateHandler() void CoreController::initApiCountryModelUpdateHandler()
{ {
// TODO
connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() { connect(m_serversModel.get(), &ServersModel::updateApiCountryModel, this, [this]() {
m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(), m_apiCountryModel->updateModel(m_serversModel->getProcessedServerData("apiAvailableCountries").toJsonArray(),
m_serversModel->getProcessedServerData("apiServerCountryCode").toString()); m_serversModel->getProcessedServerData("apiServerCountryCode").toString());
}); });
connect(m_serversModel.get(), &ServersModel::updateApiServicesModel, this,
[this]() { m_apiServicesModel->updateModel(m_serversModel->getProcessedServerData("apiConfig").toJsonObject()); });
} }
void CoreController::initContainerModelUpdateHandler() void CoreController::initContainerModelUpdateHandler()
-5
View File
@@ -5,10 +5,6 @@
#include <QQmlContext> #include <QQmlContext>
#include <QThread> #include <QThread>
#ifndef Q_OS_ANDROID
#include "ui/systemtray_notificationhandler.h"
#endif
#include "ui/controllers/api/apiConfigsController.h" #include "ui/controllers/api/apiConfigsController.h"
#include "ui/controllers/api/apiSettingsController.h" #include "ui/controllers/api/apiSettingsController.h"
#include "ui/controllers/api/apiPremV1MigrationController.h" #include "ui/controllers/api/apiPremV1MigrationController.h"
@@ -65,7 +61,6 @@ public:
signals: signals:
void translationsUpdated(); void translationsUpdated();
void websiteUrlChanged(const QString &newUrl);
private: private:
void initModels(); void initModels();
+50
View File
@@ -0,0 +1,50 @@
<?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>
+50 -66
View File
@@ -1,12 +1,28 @@
#include "xrayprotocol.h" #include "xrayprotocol.h"
#include "core/defs.h"
#include "core/networkUtilities.h"
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <QtCore/qcontainerfwd.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlogging.h>
#include <QtCore/qobject.h>
#include <arpa/inet.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include "core/networkUtilities.h" #include "core/ipcclient.h"
#include "utilities.h" #include "libxray.h"
XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent) XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) : VpnProtocol(configuration, parent)
{ {
@@ -15,6 +31,9 @@ XrayProtocol::XrayProtocol(const QJsonObject &configuration, QObject *parent) :
m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr; m_vpnGateway = amnezia::protocols::xray::defaultLocalAddr;
m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr; m_vpnLocalAddress = amnezia::protocols::xray::defaultLocalAddr;
m_t2sProcess = IpcClient::InterfaceTun2Socks(); m_t2sProcess = IpcClient::InterfaceTun2Socks();
amnezia_xray_setloghandler(&XrayProtocol::ctxLogHandler, this);
amnezia_xray_setsockcallback(&XrayProtocol::ctxSockCallback, this);
} }
XrayProtocol::~XrayProtocol() XrayProtocol::~XrayProtocol()
@@ -25,61 +44,20 @@ XrayProtocol::~XrayProtocol()
ErrorCode XrayProtocol::start() ErrorCode XrayProtocol::start()
{ {
qDebug().noquote() << "XrayProtocol xrayExecPath():" << xrayExecPath(); qDebug() << "XrayProtocol::start()";
if (!QFileInfo::exists(xrayExecPath())) { auto cfg = QJsonDocument(m_xrayConfig).toJson().toStdString();
setLastError(ErrorCode::XrayExecutableMissing); if (auto err = amnezia_xray_configure(cfg.data()); err != 0) {
return lastError(); return ErrorCode::InternalError;
} }
#ifdef QT_DEBUG if (auto err = amnezia_xray_start(); err != 0) {
m_xrayCfgFile.setAutoRemove(false); return ErrorCode::NotImplementedError;
#endif
m_xrayCfgFile.open();
QString config = QJsonDocument(m_xrayConfig).toJson();
config.replace(m_remoteHost, m_remoteAddress);
m_xrayCfgFile.write(config.toUtf8());
m_xrayCfgFile.close();
QStringList args = QStringList() << "-c" << m_xrayCfgFile.fileName() << "-format=json";
qDebug().noquote() << "XrayProtocol::start()" << xrayExecPath() << args.join(" ");
m_xrayProcess.setProcessChannelMode(QProcess::MergedChannels);
m_xrayProcess.setProgram(xrayExecPath());
if (Utils::processIsRunning(Utils::executable("xray", false))) {
qDebug().noquote() << "kill previos xray";
Utils::killProcessByName(Utils::executable("xray", false));
} }
m_xrayProcess.setArguments(args); setConnectionState(Vpn::ConnectionState::Connecting);
connect(&m_xrayProcess, &QProcess::readyReadStandardOutput, this, [this]() { return startTun2Sock();
#ifdef QT_DEBUG
qDebug().noquote() << "xray:" << m_xrayProcess.readAllStandardOutput();
#endif
});
connect(&m_xrayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
[this](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug().noquote() << "XrayProtocol finished, exitCode, exitStatus" << exitCode << exitStatus;
setConnectionState(Vpn::ConnectionState::Disconnected);
if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) {
emit protocolError(amnezia::ErrorCode::XrayExecutableCrashed);
emit setConnectionState(Vpn::ConnectionState::Error);
}
});
m_xrayProcess.start();
m_xrayProcess.waitForStarted();
if (m_xrayProcess.state() == QProcess::ProcessState::Running) {
setConnectionState(Vpn::ConnectionState::Connecting);
QThread::msleep(1000);
return startTun2Sock();
} else
return ErrorCode::XrayExecutableMissing;
} }
ErrorCode XrayProtocol::startTun2Sock() ErrorCode XrayProtocol::startTun2Sock()
@@ -126,9 +104,7 @@ ErrorCode XrayProtocol::startTun2Sock()
} }
#endif #endif
if (m_routeMode == Settings::RouteMode::VpnAllSites) { if (m_routeMode == Settings::RouteMode::VpnAllSites) {
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "0.0.0.0/1"); IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "1.0.0.0/8" << "2.0.0.0/7" << "4.0.0.0/6" << "8.0.0.0/5" << "16.0.0.0/4" << "32.0.0.0/3" << "64.0.0.0/2" << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_vpnGateway, QStringList() << "128.0.0.0/1");
IpcClient::Interface()->routeAddList(m_routeGateway, QStringList() << m_remoteAddress);
} }
IpcClient::Interface()->StopRoutingIpv6(); IpcClient::Interface()->StopRoutingIpv6();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -171,9 +147,9 @@ void XrayProtocol::stop()
IpcClient::Interface()->StartRoutingIpv6(); IpcClient::Interface()->StartRoutingIpv6();
#endif #endif
qDebug() << "XrayProtocol::stop()"; qDebug() << "XrayProtocol::stop()";
m_xrayProcess.disconnect();
m_xrayProcess.kill(); amnezia_xray_stop();
m_xrayProcess.waitForFinished(3000);
if (m_t2sProcess) { if (m_t2sProcess) {
m_t2sProcess->stop(); m_t2sProcess->stop();
} }
@@ -181,15 +157,6 @@ void XrayProtocol::stop()
setConnectionState(Vpn::ConnectionState::Disconnected); setConnectionState(Vpn::ConnectionState::Disconnected);
} }
QString XrayProtocol::xrayExecPath()
{
#ifdef Q_OS_WIN
return Utils::executable(QString("xray/xray"), true);
#else
return Utils::executable(QString("xray"), true);
#endif
}
void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration) void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
{ {
m_configData = configuration; m_configData = configuration;
@@ -205,3 +172,20 @@ void XrayProtocol::readXrayConfiguration(const QJsonObject &configuration)
m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString(); m_primaryDNS = configuration.value(amnezia::config_key::dns1).toString();
m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString(); m_secondaryDNS = configuration.value(amnezia::config_key::dns2).toString();
} }
void XrayProtocol::sockCallback(uintptr_t fd)
{
// #ifdef Q_OS_DARWIN
// const int iface = if_nametoindex("en0");
// if (iface > 0)
// {
// setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface, sizeof(iface));
// setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &iface, sizeof(iface));
// }
// #endif
}
void XrayProtocol::logHandler(char* str)
{
}
+12 -5
View File
@@ -3,9 +3,10 @@
#include "QProcess" #include "QProcess"
#include "containers/containers_defs.h" #include "protocols/vpnprotocol.h"
#include "openvpnprotocol.h" #include "rep_ipc_process_tun2socks_replica.h"
#include "settings.h" #include "settings.h"
#include <cstdint>
class XrayProtocol : public VpnProtocol class XrayProtocol : public VpnProtocol
{ {
@@ -24,10 +25,16 @@ protected:
QJsonObject m_xrayConfig; QJsonObject m_xrayConfig;
private: private:
static QString xrayExecPath(); static void ctxSockCallback(uintptr_t fd, void* ctx) {
static QString tun2SocksExecPath(); reinterpret_cast<XrayProtocol*>(ctx)->sockCallback(fd);
}
static void ctxLogHandler(char* str, void* ctx) {
reinterpret_cast<XrayProtocol*>(ctx)->logHandler(str);
}
void sockCallback(uintptr_t fd);
void logHandler(char* str);
private:
int m_localPort; int m_localPort;
QString m_remoteHost; QString m_remoteHost;
QString m_remoteAddress; QString m_remoteAddress;
+1 -3
View File
@@ -127,7 +127,7 @@
<file>ui/qml/Components/SelectLanguageDrawer.qml</file> <file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Components/ServersListView.qml</file> <file>ui/qml/Components/ServersListView.qml</file>
<file>ui/qml/Components/SettingsContainersListView.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/TransportProtoSelector.qml</file>
<file>ui/qml/Components/AddSitePanel.qml</file> <file>ui/qml/Components/AddSitePanel.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file> <file>ui/qml/Config/GlobalConfig.qml</file>
@@ -228,7 +228,6 @@
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file> <file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>ui/qml/Pages2/PageShare.qml</file> <file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Pages2/PageShareFullAccess.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/Pages2/PageStart.qml</file>
<file>ui/qml/Components/RenameServerDrawer.qml</file> <file>ui/qml/Components/RenameServerDrawer.qml</file>
<file>ui/qml/Controls2/ListViewType.qml</file> <file>ui/qml/Controls2/ListViewType.qml</file>
@@ -241,7 +240,6 @@
<file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file> <file>ui/qml/Components/ApiPremV1SubListDrawer.qml</file>
<file>ui/qml/Components/OtpCodeDrawer.qml</file> <file>ui/qml/Components/OtpCodeDrawer.qml</file>
<file>ui/qml/Components/AwgTextField.qml</file> <file>ui/qml/Components/AwgTextField.qml</file>
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
</qresource> </qresource>
<qresource prefix="/countriesFlags"> <qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file> <file>images/flagKit/ZW.svg</file>
+1 -1
View File
@@ -1,7 +1,7 @@
FROM alpine:3.15 FROM alpine:3.15
LABEL maintainer="AmneziaVPN" LABEL maintainer="AmneziaVPN"
ARG XRAY_RELEASE="v25.8.3" ARG XRAY_RELEASE="v1.8.6"
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
RUN apk --update upgrade --no-cache RUN apk --update upgrade --no-cache
File diff suppressed because it is too large Load Diff
@@ -248,10 +248,10 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
apiConfigObject.value(configKey::userCountryCode).toString(), apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode, serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(), apiConfigObject.value(configKey::serviceType).toString(),
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(), m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() }; serverConfigObject.value(configKey::authData).toObject() };
QString protocol = gatewayRequestData.serviceProtocol; QString protocol = apiConfigObject.value(configKey::serviceProtocol).toString();
ProtocolData protocolData = generateProtocolData(protocol); ProtocolData protocolData = generateProtocolData(protocol);
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = gatewayRequestData.toJsonObject();
@@ -283,7 +283,7 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode)
apiConfigObject.value(configKey::userCountryCode).toString(), apiConfigObject.value(configKey::userCountryCode).toString(),
serverCountryCode, serverCountryCode,
apiConfigObject.value(configKey::serviceType).toString(), apiConfigObject.value(configKey::serviceType).toString(),
configKey::awg, // apiConfigObject.value(configKey::serviceProtocol).toString(), m_apiServicesModel->getSelectedServiceProtocol(),
serverConfigObject.value(configKey::authData).toObject() }; serverConfigObject.value(configKey::authData).toObject() };
QJsonObject apiPayload = gatewayRequestData.toJsonObject(); QJsonObject apiPayload = gatewayRequestData.toJsonObject();
+5 -11
View File
@@ -4,12 +4,6 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQuickWindow> #include <QQuickWindow>
#include "logger.h"
namespace {
Logger logger("FocusController");
}
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent) FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
: QObject { parent }, : QObject { parent },
m_engine { engine }, m_engine { engine },
@@ -91,7 +85,7 @@ void FocusController::dropRootObject(QObject *object)
dropListView(); dropListView();
setFocusOnDefaultItem(); setFocusOnDefaultItem();
} else { } else {
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object; qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
} }
} }
@@ -107,7 +101,7 @@ void FocusController::reload(Direction direction)
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top()); QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
if (!rootObject) { if (!rootObject) {
logger.error() << "No ROOT OBJECT found!"; qCritical() << "No ROOT OBJECT found!";
resetRootObject(); resetRootObject();
dropListView(); dropListView();
return; return;
@@ -119,7 +113,7 @@ void FocusController::reload(Direction direction)
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore); direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
logger.warning() << "Focus chain is empty!"; qWarning() << "Focus chain is empty!";
resetRootObject(); resetRootObject();
dropListView(); dropListView();
return; return;
@@ -137,7 +131,7 @@ void FocusController::nextItem(Direction direction)
} }
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
logger.warning() << "There are no items to navigate"; qWarning() << "There are no items to navigate";
setFocusOnDefaultItem(); setFocusOnDefaultItem();
return; return;
} }
@@ -155,7 +149,7 @@ void FocusController::nextItem(Direction direction)
const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex)); const auto focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(focusedItemIndex));
if (focusedItem == nullptr) { if (focusedItem == nullptr) {
logger.warning() << "Failed to get item to focus on. Setting focus on default"; qWarning() << "Failed to get item to focus on. Setting focus on default";
setFocusOnDefaultItem(); setFocusOnDefaultItem();
return; return;
} }
+2 -97
View File
@@ -403,19 +403,9 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
QJsonObject config; QJsonObject config;
Proto mainProto = ContainerProps::defaultProtocol(container); Proto mainProto = ContainerProps::defaultProtocol(container);
const auto &protocols = ContainerProps::protocolsForContainer(container); const auto &protocols = ContainerProps::protocolsForContainer(container);
for (const auto &protocol : protocols) { for (const auto &protocol : protocols) {
QJsonObject containerConfig; 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::port, port);
containerConfig.insert(config_key::transport_proto, transportProto); containerConfig.insert(config_key::transport_proto, transportProto);
@@ -560,97 +550,14 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
qDebug() << siteName; qDebug() << siteName;
containerConfig.insert(config_key::site, 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(config_key::container, ContainerProps::containerToString(container));
} }
if (shouldProcessProtocol) { config.insert(ProtocolProps::protoToString(protocol), containerConfig);
config.insert(ProtocolProps::protoToString(protocol), containerConfig);
}
} }
installedContainers.insert(container, config); installedContainers.insert(container, config);
} }
const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*"); const static QRegularExpression torOrDnsRegExp("(amnezia-(?:torwebsite|dns)).*?([0-9]*)/(udp|tcp).*");
QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo); QRegularExpressionMatch torOrDnsRegMatch = torOrDnsRegExp.match(containerInfo);
if (torOrDnsRegMatch.hasMatch()) { if (torOrDnsRegMatch.hasMatch()) {
@@ -814,8 +721,6 @@ void InstallController::clearCachedProfile(QSharedPointer<ServerController> serv
m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController); m_clientManagementModel->revokeClient(containerConfig, container, serverCredentials, serverIndex, serverController);
emit cachedProfileCleared(tr("%1 cached profile cleared").arg(ContainerProps::containerHumanNames().value(container))); 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() QRegularExpression InstallController::ipAddressPortRegExp()
@@ -83,8 +83,6 @@ signals:
void noInstalledContainers(); void noInstalledContainers();
void profileCleared(const QJsonObject &config);
private: private:
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers, void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController, const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
@@ -37,7 +37,7 @@ void ListViewFocusController::viewAtCurrentIndex() const
} }
case Section::Delegate: { case Section::Delegate: {
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
Q_ARG(int, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition) Q_ARG(int, 2)); // PositionMode (0 = Visible)
break; break;
} }
case Section::Footer: { case Section::Footer: {
-2
View File
@@ -38,7 +38,6 @@ namespace PageLoader
PageSettingsApiInstructions, PageSettingsApiInstructions,
PageSettingsApiNativeConfigs, PageSettingsApiNativeConfigs,
PageSettingsApiDevices, PageSettingsApiDevices,
PageSettingsApiSubscriptionKey,
PageSettingsKillSwitchExceptions, PageSettingsKillSwitchExceptions,
PageServiceSftpSettings, PageServiceSftpSettings,
@@ -72,7 +71,6 @@ namespace PageLoader
PageProtocolAwgClientSettings, PageProtocolAwgClientSettings,
PageShareFullAccess, PageShareFullAccess,
PageShareConnection,
PageDevMenu PageDevMenu
}; };
+4 -43
View File
@@ -35,23 +35,6 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
#endif #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) void SettingsController::toggleAmneziaDns(bool enable)
{ {
m_settings->setUseAmneziaDns(enable); m_settings->setUseAmneziaDns(enable);
@@ -147,10 +130,7 @@ void SettingsController::backupAppConfig(const QString &fileName)
QJsonDocument doc = QJsonDocument::fromJson(data); QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject config = doc.object(); QJsonObject config = doc.object();
config["AppPlatform"] = getPlatformName();
config["Conf/autoStart"] = Autostart::isAutostart(); config["Conf/autoStart"] = Autostart::isAutostart();
config["Conf/killSwitchEnabled"] = isKillSwitchEnabled();
config["Conf/strictKillSwitchEnabled"] = isStrictKillSwitchEnabled();
SystemController::saveFile(fileName, QJsonDocument(config).toJson()); SystemController::saveFile(fileName, QJsonDocument(config).toJson());
} }
@@ -175,41 +155,21 @@ void SettingsController::restoreAppConfigFromData(const QByteArray &data)
} }
toggleAutoStart(autoStart); toggleAutoStart(autoStart);
#endif #endif
m_serversModel->resetModel(); m_serversModel->resetModel();
m_languageModel->changeLanguage( m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex())); static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) #if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt(); int appSplitTunnelingRouteMode = newConfigData.value("Conf/appsRouteMode").toInt();
bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toString().toLower() == "true"; bool appSplittunnelingEnabled = newConfigData.value("Conf/appsSplitTunnelingEnabled").toBool();
m_appSplitTunnelingModel->setRouteMode(appSplitTunnelingRouteMode); 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); m_appSplitTunnelingModel->toggleSplitTunneling(appSplittunnelingEnabled);
#endif #endif
int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt(); int siteSplitTunnelingRouteMode = newConfigData.value("Conf/routeMode").toInt();
bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toString().toLower() == "true"; bool siteSplittunnelingEnabled = newConfigData.value("Conf/sitesSplitTunnelingEnabled").toBool();
m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode); m_sitesModel->setRouteMode(siteSplitTunnelingRouteMode);
m_sitesModel->toggleSplitTunneling(siteSplittunnelingEnabled); 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(); emit restoreBackupFinished();
} else { } else {
emit changeSettingsErrorOccurred(tr("Backup file is corrupted")); emit changeSettingsErrorOccurred(tr("Backup file is corrupted"));
@@ -225,7 +185,8 @@ void SettingsController::clearSettings()
{ {
m_settings->clearSettings(); m_settings->clearSettings();
m_serversModel->resetModel(); m_serversModel->resetModel();
m_languageModel->changeLanguage(m_languageModel->getSystemLanguageEnum()); m_languageModel->changeLanguage(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageModel->getCurrentLanguageIndex()));
m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites); m_sitesModel->setRouteMode(Settings::RouteMode::VpnOnlyForwardSites);
m_sitesModel->toggleSplitTunneling(false); m_sitesModel->toggleSplitTunneling(false);
@@ -136,8 +136,6 @@ private:
QString m_appVersion; QString m_appVersion;
QString getPlatform();
QDateTime m_loggingDisableDate; QDateTime m_loggingDisableDate;
bool m_isDevModeEnabled = false; bool m_isDevModeEnabled = false;
@@ -78,13 +78,6 @@ void SitesController::removeSite(int index)
emit finished(tr("Site removed: %1").arg(hostname)); 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) void SitesController::importSites(const QString &fileName, bool replaceExisting)
{ {
QByteArray jsonData; QByteArray jsonData;
-1
View File
@@ -19,7 +19,6 @@ public slots:
void addSite(QString hostname); void addSite(QString hostname);
void removeSite(int index); void removeSite(int index);
void removeSites();
void importSites(const QString &fileName, bool replaceExisting); void importSites(const QString &fileName, bool replaceExisting);
void exportSites(const QString &fileName); void exportSites(const QString &fileName);
+1 -1
View File
@@ -112,7 +112,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
} else { } else {
return tr("VPN will open only popular sites blocked in your region, such as Instagram, Facebook, Twitter and others. " 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, " "Other sites will be opened from your real IP address, "
"<a href=\"%1\" style=\"color: #FBB26A;\">more details on the website.</a>"); "<a href=\"%1/free\" style=\"color: #FBB26A;\">more details on the website.</a>");
} }
} }
case PriceRole: { case PriceRole: {
+6 -13
View File
@@ -26,12 +26,12 @@ QVariant AppSplitTunnelingModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
switch (role) { switch (role) {
case AppPathRole: { case AppPathRole: {
return m_apps.at(index.row()).appName; return m_apps.at(index.row()).appName;
} }
default: { default: {
return true; return true;
} }
} }
return QVariant(); return QVariant();
@@ -59,13 +59,6 @@ void AppSplitTunnelingModel::removeApp(QModelIndex index)
endRemoveRows(); endRemoveRows();
} }
void AppSplitTunnelingModel::clearAppsList() {
beginResetModel();
m_apps.clear();
m_settings->setVpnApps(m_currentRouteMode, m_apps);
endResetModel();
}
int AppSplitTunnelingModel::getRouteMode() int AppSplitTunnelingModel::getRouteMode()
{ {
return m_currentRouteMode; return m_currentRouteMode;
@@ -29,7 +29,6 @@ public:
public slots: public slots:
bool addApp(const InstalledAppInfo &appInfo); bool addApp(const InstalledAppInfo &appInfo);
void removeApp(QModelIndex index); void removeApp(QModelIndex index);
void clearAppsList();
int getRouteMode(); int getRouteMode();
void setRouteMode(int routeMode); void setRouteMode(int routeMode);
-27
View File
@@ -101,23 +101,6 @@ QString LanguageModel::getCurrentLanguageName()
return m_availableLanguages[getCurrentLanguageIndex()].name; 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) QString LanguageModel::getCurrentSiteUrl(const QString &path)
{ {
auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex()); auto language = static_cast<LanguageSettings::AvailableLanguageEnum>(getCurrentLanguageIndex());
@@ -127,13 +110,3 @@ QString LanguageModel::getCurrentSiteUrl(const QString &path)
default: return QString("https://amnezia.org") + (path.isEmpty() ? "" : (QString("/%1").arg(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)));
}
}
-2
View File
@@ -46,7 +46,6 @@ public:
}; };
LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr); LanguageModel(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
LanguageSettings::AvailableLanguageEnum getSystemLanguageEnum();
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -61,7 +60,6 @@ public slots:
int getLineHeightAppend(); int getLineHeightAppend();
QString getCurrentLanguageName(); QString getCurrentLanguageName();
QString getCurrentSiteUrl(const QString &path = ""); QString getCurrentSiteUrl(const QString &path = "");
QString getCurrentDocsUrl(const QString &path = "");
signals: signals:
void updateTranslations(const QLocale &locale); void updateTranslations(const QLocale &locale);
-10
View File
@@ -83,16 +83,6 @@ void SitesModel::removeSite(QModelIndex index)
endRemoveRows(); endRemoveRows();
} }
void SitesModel::removeSites()
{
beginResetModel();
m_settings->removeAllVpnSites(m_currentRouteMode);
fillSites();
endResetModel();
}
int SitesModel::getRouteMode() int SitesModel::getRouteMode()
{ {
return m_currentRouteMode; return m_currentRouteMode;
-1
View File
@@ -28,7 +28,6 @@ public slots:
bool addSite(const QString &hostname, const QString &ip); bool addSite(const QString &hostname, const QString &ip);
void addSites(const QMap<QString, QString> &sites, bool replaceExisting); void addSites(const QMap<QString, QString> &sites, bool replaceExisting);
void removeSite(QModelIndex index); void removeSite(QModelIndex index);
void removeSites();
int getRouteMode(); int getRouteMode();
void setRouteMode(int routeMode); void setRouteMode(int routeMode);
@@ -10,7 +10,8 @@ import ProtocolEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType {
ListView {
id: menuContent id: menuContent
property var rootWidth property var rootWidth
@@ -20,6 +21,13 @@ ListViewType {
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
clip: true
snapMode: ListView.SnapToItem
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
ButtonGroup { ButtonGroup {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }
@@ -1,97 +1,97 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
DrawerType2 { DrawerType2 {
id: root id: root
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
expandedStateContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
headerText: qsTr("Split tunneling") headerText: qsTr("Split tunneling")
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others") descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
} }
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingSwitch id: splitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
text: qsTr("Split tunneling on the server") text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server") descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
} }
LabelWithButtonType { LabelWithButtonType {
id: siteBasedSplitTunnelingSwitch id: siteBasedSplitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
} }
LabelWithButtonType { LabelWithButtonType {
id: appSplitTunnelingSwitch id: appSplitTunnelingSwitch
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("App-based split tunneling") text: qsTr("App-based split tunneling")
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
} }
} }
} }
@@ -57,7 +57,7 @@ DrawerType2 {
headerText: qsTr("Choose application") headerText: qsTr("Choose application")
} }
ListViewType { ListView {
id: listView id: listView
Layout.fillWidth: true Layout.fillWidth: true
@@ -66,6 +66,11 @@ DrawerType2 {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
clip: true
interactive: true
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyInstalledAppsModel id: proxyInstalledAppsModel
sourceModel: installedAppsModel sourceModel: installedAppsModel
@@ -76,35 +81,44 @@ DrawerType2 {
} }
} }
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
RowLayout { ColumnLayout {
CheckBoxType { id: delegateContent
Layout.fillWidth: true
text: appName anchors.fill: parent
checked: isAppSelected
onCheckedChanged: { RowLayout {
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked) 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
} }
} }
Image { DividerType {}
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
}
} }
DividerType {}
} }
} }
} }
@@ -49,7 +49,7 @@ DrawerType2 {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
@@ -57,8 +57,14 @@ DrawerType2 {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
property bool isFocusable: true
property int selectedIndex: LanguageModel.currentLanguageIndex property int selectedIndex: LanguageModel.currentLanguageIndex
clip: true
reuseItems: true
ScrollBar.vertical: ScrollBarType {}
model: LanguageModel model: LanguageModel
ButtonGroup { ButtonGroup {
+8 -1
View File
@@ -15,7 +15,7 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
ListViewType { ListView {
id: root id: root
property int selectedIndex: ServersModel.defaultIndex property int selectedIndex: ServersModel.defaultIndex
@@ -28,6 +28,10 @@ ListViewType {
model: ServersModel model: ServersModel
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
Connections { Connections {
target: ServersModel target: ServersModel
function onDefaultServerIndexChanged(serverIndex) { function onDefaultServerIndexChanged(serverIndex) {
@@ -35,6 +39,9 @@ ListViewType {
} }
} }
clip: true
reuseItems: true
delegate: Item { delegate: Item {
id: menuContentDelegate id: menuContentDelegate
objectName: "menuContentDelegate" objectName: "menuContentDelegate"
@@ -13,64 +13,78 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType { ListView {
id: root id: root
anchors.fill: parent width: parent.width
height: root.contentItem.height
delegate: ColumnLayout { clip: true
width: root.width reuseItems: true
LabelWithButtonType { property bool isFocusable: false
Layout.fillWidth: true
text: name delegate: Item {
descriptionText: description implicitWidth: root.width
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg" implicitHeight: delegateContent.implicitHeight
clickedFunction: function() { ColumnLayout {
if (isInstalled) { id: delegateContent
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
if (serviceType !== ProtocolEnum.Other) { anchors.fill: parent
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
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: {
ProtocolsModel.updateModel(config) ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolRaw) PageController.goToPage(PageEnum.PageProtocolRaw)
return break
}
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
} }
}
switch (containerIndex) { } else {
case ContainerEnum.Ipsec: { ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
ProtocolsModel.updateModel(config) InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageProtocolRaw) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
break
}
case ContainerEnum.Dns: {
PageController.goToPage(PageEnum.PageServiceDnsSettings)
break
}
default: {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
}
} }
}
} else { MouseArea {
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index)) anchors.fill: parent
InstallController.setShouldCreateServer(false) cursorShape: Qt.PointingHandCursor
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) enabled: false
} }
} }
MouseArea { DividerType {}
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
} }
DividerType {}
} }
} }
@@ -0,0 +1,375 @@
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\"")
}
}
}
}
}
+220 -210
View File
@@ -1,210 +1,220 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string hoveredColor: AmneziaStyle.color.lightGray property string hoveredColor: AmneziaStyle.color.lightGray
property string defaultColor: AmneziaStyle.color.paleGray property string defaultColor: AmneziaStyle.color.paleGray
property string disabledColor: AmneziaStyle.color.charcoalGray property string disabledColor: AmneziaStyle.color.charcoalGray
property string pressedColor: AmneziaStyle.color.mutedGray property string pressedColor: AmneziaStyle.color.mutedGray
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string borderColor: AmneziaStyle.color.paleGray property string borderColor: AmneziaStyle.color.paleGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderWidth: 0 property int borderWidth: 0
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string leftImageSource property string leftImageSource
property string rightImageSource property string rightImageSource
property string leftImageColor: textColor property string leftImageColor: textColor
property bool changeLeftImageSize: true property bool changeLeftImageSize: true
property bool squareLeftSide: false property bool squareLeftSide: false
property var clickedFunc property FlickableType parentFlickable
property alias buttonTextLabel: buttonText property var clickedFunc
property bool isFocusable: true property alias buttonTextLabel: buttonText
Keys.onTabPressed: { property bool isFocusable: true
FocusController.nextKeyTabItem()
} Keys.onTabPressed: {
FocusController.nextKeyTabItem()
Keys.onBacktabPressed: { }
FocusController.previousKeyTabItem()
} Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
Keys.onUpPressed: { }
FocusController.nextKeyUpItem()
} Keys.onUpPressed: {
FocusController.nextKeyUpItem()
Keys.onDownPressed: { }
FocusController.nextKeyDownItem()
} Keys.onDownPressed: {
FocusController.nextKeyDownItem()
Keys.onLeftPressed: { }
FocusController.nextKeyLeftItem()
} Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
Keys.onRightPressed: { }
FocusController.nextKeyRightItem()
} Keys.onRightPressed: {
FocusController.nextKeyRightItem()
implicitHeight: 56 }
hoverEnabled: true implicitHeight: 56
background: Rectangle { hoverEnabled: true
id: focusBorder
onFocusChanged: {
color: AmneziaStyle.color.transparent if (root.activeFocus) {
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent if (root.parentFlickable) {
border.width: root.activeFocus ? root.borderFocusedWidth : 0 root.parentFlickable.ensureVisible(this)
}
anchors.fill: parent }
}
radius: 16
background: Rectangle {
Rectangle { id: focusBorder
id: background
color: AmneziaStyle.color.transparent
anchors.fill: focusBorder border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
anchors.margins: root.activeFocus ? 2 : 0 border.width: root.activeFocus ? root.borderFocusedWidth : 0
radius: root.activeFocus ? 14 : 16 anchors.fill: parent
color: {
if (root.enabled) { radius: 16
if (root.pressed) {
return pressedColor Rectangle {
} id: background
return root.hovered ? hoveredColor : defaultColor
} else { anchors.fill: focusBorder
return disabledColor anchors.margins: root.activeFocus ? 2 : 0
}
} radius: root.activeFocus ? 14 : 16
border.color: borderColor color: {
border.width: borderWidth if (root.enabled) {
if (root.pressed) {
Behavior on color { return pressedColor
PropertyAnimation { duration: 200 } }
} return root.hovered ? hoveredColor : defaultColor
} else {
Rectangle { return disabledColor
visible: root.squareLeftSide }
}
z: 1 border.color: borderColor
border.width: borderWidth
width: parent.radius
height: parent.radius Behavior on color {
anchors.top: parent.top PropertyAnimation { duration: 200 }
anchors.bottom: parent.bottom }
anchors.left: parent.left
color: { Rectangle {
if (root.enabled) { visible: root.squareLeftSide
if (root.pressed) {
return pressedColor z: 1
}
return root.hovered ? hoveredColor : defaultColor width: parent.radius
} else { height: parent.radius
return disabledColor anchors.top: parent.top
} anchors.bottom: parent.bottom
} anchors.left: parent.left
color: {
Behavior on color { if (root.enabled) {
PropertyAnimation { duration: 200 } if (root.pressed) {
} return pressedColor
} }
} return root.hovered ? hoveredColor : defaultColor
} } else {
return disabledColor
MouseArea { }
anchors.fill: focusBorder }
enabled: false
cursorShape: Qt.PointingHandCursor Behavior on color {
} PropertyAnimation { duration: 200 }
}
contentItem: Item { }
anchors.fill: focusBorder }
}
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight MouseArea {
anchors.fill: focusBorder
RowLayout { enabled: false
id: content cursorShape: Qt.PointingHandCursor
anchors.centerIn: parent }
Image { contentItem: Item {
id: leftImage anchors.fill: focusBorder
source: root.leftImageSource
visible: root.leftImageSource === "" ? false : true implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
layer {
enabled: leftImageColor !== "" ? true : false RowLayout {
effect: ColorOverlay { id: content
color: leftImageColor anchors.centerIn: parent
}
} Image {
id: leftImage
Component.onCompleted: { source: root.leftImageSource
if (root.changeLeftImageSize) { visible: root.leftImageSource === "" ? false : true
leftImage.Layout.preferredHeight = 20
leftImage.Layout.preferredWidth = 20 layer {
} enabled: leftImageColor !== "" ? true : false
} effect: ColorOverlay {
} color: leftImageColor
}
ButtonTextType { }
id: buttonText
Component.onCompleted: {
color: root.textColor if (root.changeLeftImageSize) {
text: root.text leftImage.Layout.preferredHeight = 20
visible: root.text === "" ? false : true leftImage.Layout.preferredWidth = 20
}
horizontalAlignment: Text.AlignLeft }
verticalAlignment: Text.AlignVCenter }
}
ButtonTextType {
Image { id: buttonText
Layout.preferredHeight: 20
Layout.preferredWidth: 20 color: root.textColor
text: root.text
source: root.rightImageSource visible: root.text === "" ? false : true
visible: root.rightImageSource === "" ? false : true
horizontalAlignment: Text.AlignLeft
layer { verticalAlignment: Text.AlignVCenter
enabled: true }
effect: ColorOverlay {
color: textColor Image {
} Layout.preferredHeight: 20
} Layout.preferredWidth: 20
}
} source: root.rightImageSource
} visible: root.rightImageSource === "" ? false : true
Keys.onEnterPressed: { layer {
if (root.clickedFunc && typeof root.clickedFunc === "function") { enabled: true
root.clickedFunc() effect: ColorOverlay {
} color: textColor
} }
}
Keys.onReturnPressed: { }
if (root.clickedFunc && typeof root.clickedFunc === "function") { }
root.clickedFunc() }
}
} Keys.onEnterPressed: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
onClicked: { root.clickedFunc()
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()
}
}
}
+203 -185
View File
@@ -1,185 +1,203 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string headerText property string headerText
property string bodyText property string bodyText
property string footerText property string footerText
property string hoveredColor: AmneziaStyle.color.slateGray property string hoveredColor: AmneziaStyle.color.slateGray
property string defaultColor: AmneziaStyle.color.onyxBlack property string defaultColor: AmneziaStyle.color.onyxBlack
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string rightImageSource property string rightImageSource
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property string leftImageSource property string leftImageSource
property real textOpacity: 1.0 property real textOpacity: 1.0
property alias focusItem: rightImage property alias focusItem: rightImage
hoverEnabled: true property FlickableType parentFlickable
background: Rectangle { hoverEnabled: true
id: backgroundRect
background: Rectangle {
anchors.fill: parent id: backgroundRect
radius: 16
anchors.fill: parent
color: defaultColor radius: 16
Behavior on color { color: defaultColor
PropertyAnimation { duration: 200 }
} Behavior on color {
} PropertyAnimation { duration: 200 }
}
contentItem: Item { }
anchors.left: parent.left
anchors.right: parent.right function ensureVisible(item) {
if (item.activeFocus) {
implicitHeight: content.implicitHeight if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
RowLayout { }
id: content }
}
anchors.fill: parent
onFocusChanged: {
Image { ensureVisible(root)
id: leftImage }
source: leftImageSource
focusItem.onFocusChanged: {
visible: leftImageSource !== "" root.ensureVisible(focusItem)
}
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.topMargin: 24 contentItem: Item {
Layout.bottomMargin: 24 anchors.left: parent.left
Layout.leftMargin: 24 anchors.right: parent.right
}
implicitHeight: content.implicitHeight
ColumnLayout {
RowLayout {
ListItemTitleType { id: content
text: root.headerText
visible: text !== "" anchors.fill: parent
Layout.fillWidth: true Image {
Layout.rightMargin: 16 id: leftImage
Layout.leftMargin: 16 source: leftImageSource
Layout.topMargin: 16
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16 visible: leftImageSource !== ""
opacity: root.textOpacity Layout.alignment: Qt.AlignLeft | Qt.AlignTop
} Layout.topMargin: 24
Layout.bottomMargin: 24
CaptionTextType { Layout.leftMargin: 24
text: root.bodyText }
visible: text !== ""
ColumnLayout {
color: AmneziaStyle.color.mutedGray
textFormat: Text.RichText ListItemTitleType {
text: root.headerText
Layout.fillWidth: true visible: text !== ""
Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.bottomMargin: root.footerText !== "" ? 0 : 16 Layout.rightMargin: 16
Layout.leftMargin: 16
opacity: root.textOpacity Layout.topMargin: 16
} Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
ButtonTextType { opacity: root.textOpacity
text: root.footerText }
visible: text !== ""
CaptionTextType {
color: AmneziaStyle.color.mutedGray text: root.bodyText
visible: text !== ""
Layout.fillWidth: true
Layout.rightMargin: 16 color: AmneziaStyle.color.mutedGray
Layout.leftMargin: 16 textFormat: Text.RichText
Layout.topMargin: 16
Layout.bottomMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
opacity: root.textOpacity Layout.leftMargin: 16
} Layout.bottomMargin: root.footerText !== "" ? 0 : 16
}
opacity: root.textOpacity
ImageButtonType { }
id: rightImage
ButtonTextType {
implicitWidth: 40 text: root.footerText
implicitHeight: 40 visible: text !== ""
hoverEnabled: false color: AmneziaStyle.color.mutedGray
image: rightImageSource
imageColor: rightImageColor Layout.fillWidth: true
visible: rightImageSource ? true : false Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.topMargin: 16
Layout.topMargin: 16 Layout.bottomMargin: 16
Layout.bottomMargin: 16
Layout.rightMargin: 16 opacity: root.textOpacity
}
Rectangle { }
id: rightImageBackground
ImageButtonType {
anchors.fill: parent id: rightImage
radius: 12
color: "transparent" implicitWidth: 40
implicitHeight: 40
Behavior on color {
PropertyAnimation { duration: 200 } hoverEnabled: false
} image: rightImageSource
} imageColor: rightImageColor
visible: rightImageSource ? true : false
onClicked: {
root.clicked() Layout.alignment: Qt.AlignRight | Qt.AlignTop
} Layout.topMargin: 16
} Layout.bottomMargin: 16
} Layout.rightMargin: 16
}
Rectangle {
MouseArea { id: rightImageBackground
anchors.fill: parent
anchors.fill: parent
cursorShape: Qt.PointingHandCursor radius: 12
hoverEnabled: true color: "transparent"
enabled: root.enabled
Behavior on color {
onEntered: { PropertyAnimation { duration: 200 }
backgroundRect.color = root.hoveredColor }
}
if (rightImageSource) {
rightImageBackground.color = rightImage.hoveredColor onClicked: {
} root.clicked()
root.textOpacity = 0.8 }
} }
}
onExited: { }
backgroundRect.color = root.defaultColor
MouseArea {
if (rightImageSource) { anchors.fill: parent
rightImageBackground.color = rightImage.defaultColor
} cursorShape: Qt.PointingHandCursor
root.textOpacity = 1 hoverEnabled: true
} enabled: root.enabled
onPressedChanged: { onEntered: {
if (rightImageSource) { backgroundRect.color = root.hoveredColor
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
} if (rightImageSource) {
root.textOpacity = 0.7 rightImageBackground.color = rightImage.hoveredColor
} }
root.textOpacity = 0.8
onClicked: { }
root.clicked()
} 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()
}
}
}
+170 -187
View File
@@ -1,187 +1,170 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
CheckBox { CheckBox {
id: root id: root
property string descriptionText property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultBorderColor: AmneziaStyle.color.paleGray property string defaultBorderColor: AmneziaStyle.color.paleGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string checkedImageColor: AmneziaStyle.color.goldenApricot property string checkedImageColor: AmneziaStyle.color.goldenApricot
property string pressedImageColor: AmneziaStyle.color.burntOrange property string pressedImageColor: AmneziaStyle.color.burntOrange
property string defaultImageColor: AmneziaStyle.color.transparent property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
property string imageSource: "qrc:/images/controls/check.svg" property string imageSource: "qrc:/images/controls/check.svg"
property bool isFocusable: true property var parentFlickable
onFocusChanged: {
Keys.onTabPressed: { if (root.activeFocus) {
FocusController.nextKeyTabItem() if (root.parentFlickable) {
} root.parentFlickable.ensureVisible(root)
}
Keys.onBacktabPressed: { }
FocusController.previousKeyTabItem() }
}
hoverEnabled: enabled ? true : false
Keys.onUpPressed: { focusPolicy: Qt.NoFocus
FocusController.nextKeyUpItem()
} background: Rectangle {
color: AmneziaStyle.color.transparent
Keys.onDownPressed: { border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
FocusController.nextKeyDownItem() border.width: 1
} radius: 16
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() indicator: Rectangle {
} id: background
Keys.onRightPressed: { anchors.verticalCenter: parent.verticalCenter
FocusController.nextKeyRightItem()
} implicitWidth: 56
implicitHeight: 56
hoverEnabled: enabled ? true : false radius: 16
focusPolicy: Qt.NoFocus
color: {
background: Rectangle { if (root.hovered) {
color: AmneziaStyle.color.transparent return hoveredColor
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent }
border.width: 1 return defaultColor
radius: 16 }
}
Behavior on color {
indicator: Rectangle { PropertyAnimation { duration: 200 }
id: background }
anchors.verticalCenter: parent.verticalCenter Rectangle {
id: imageBorder
implicitWidth: 56
implicitHeight: 56 anchors.centerIn: parent
radius: 16 width: 24
height: 24
color: { color: AmneziaStyle.color.transparent
if (root.hovered) { border.color: root.checked ?
return hoveredColor (root.enabled ?
} checkedBorderColor :
return defaultColor checkedBorderDisabledColor) :
} defaultBorderColor
border.width: 1
Behavior on color { radius: 4
PropertyAnimation { duration: 200 }
} Image {
anchors.centerIn: parent
Rectangle {
id: imageBorder source: root.pressed ? imageSource : root.checked ? imageSource : ""
layer {
anchors.centerIn: parent enabled: true
width: 24 effect: ColorOverlay {
height: 24 color: {
color: AmneziaStyle.color.transparent if (root.pressed) {
border.color: root.checked ? return root.pressedImageColor
(root.enabled ? } else if (root.checked) {
checkedBorderColor : if (root.enabled) {
checkedBorderDisabledColor) : return root.checkedImageColor
defaultBorderColor } else {
border.width: 1 return root.checkedDisabledImageColor
radius: 4 }
} else {
Image { return root.defaultImageColor
anchors.centerIn: parent }
}
source: root.pressed ? imageSource : root.checked ? imageSource : "" }
layer { }
enabled: true }
effect: ColorOverlay { }
color: { }
if (root.pressed) {
return root.pressedImageColor contentItem: Item {
} else if (root.checked) { anchors.left: parent.left
if (root.enabled) { anchors.right: parent.right
return root.checkedImageColor anchors.leftMargin: 8 + background.width
} else {
return root.checkedDisabledImageColor implicitHeight: content.implicitHeight
}
} else { ColumnLayout {
return root.defaultImageColor id: content
}
} anchors.left: parent.left
} anchors.right: parent.right
} anchors.verticalCenter: parent.verticalCenter
}
} spacing: 4
}
ListItemTitleType {
contentItem: Item { Layout.fillWidth: true
anchors.left: parent.left
anchors.right: parent.right text: root.text
anchors.leftMargin: 8 + background.width color: root.enabled ? root.textColor : root.textDisabledColor
}
implicitHeight: content.implicitHeight
CaptionTextType {
ColumnLayout { id: description
id: content
Layout.fillWidth: true
anchors.left: parent.left
anchors.right: parent.right text: root.descriptionText
anchors.verticalCenter: parent.verticalCenter color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
spacing: 4 visible: root.descriptionText !== ""
}
ListItemTitleType { }
Layout.fillWidth: true }
text: root.text MouseArea {
color: root.enabled ? root.textColor : root.textDisabledColor anchors.fill: parent
} cursorShape: Qt.PointingHandCursor
enabled: false
CaptionTextType { }
id: description
Layout.fillWidth: true Keys.onEnterPressed: {
root.checked = !root.checked
text: root.descriptionText }
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
Keys.onReturnPressed: {
visible: root.descriptionText !== "" root.checked = !root.checked
} }
}
} }
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
root.checked = !root.checked
}
Keys.onReturnPressed: {
root.checked = !root.checked
}
}
@@ -27,5 +27,6 @@ Flickable {
ScrollBar.vertical: ScrollBarType { ScrollBar.vertical: ScrollBarType {
id: scrollBar id: scrollBar
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
} }
} }
+328 -309
View File
@@ -1,309 +1,328 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
// property alias focusObjectName: eyeImage.objectName property string text
property string text property int textMaximumLineCount: 2
property int textMaximumLineCount: 2 property int textElide: Qt.ElideRight
property int textElide: Qt.ElideRight
property string descriptionText
property string descriptionText
property var clickedFunction
property var clickedFunction
property string buttonImageSource
property string buttonImageSource property string rightImageSource
property string rightImageSource property string leftImageSource
property string leftImageSource property bool isLeftImageHoverEnabled: true
property bool isLeftImageHoverEnabled: true property bool isSmallLeftImage: false
property bool isSmallLeftImage: false
property alias rightButton: rightImage
property alias rightButton: rightImage property alias eyeButton: eyeImage
property alias eyeButton: eyeImage property FlickableType parentFlickable
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string descriptionColor: AmneziaStyle.color.mutedGray property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property real textOpacity: 1.0 property real textOpacity: 1.0
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property bool descriptionOnTop: false property bool descriptionOnTop: false
property bool hideDescription: true property bool hideDescription: true
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
MouseArea { onFocusChanged: {
anchors.fill: parent if (root.activeFocus) {
cursorShape: Qt.PointingHandCursor if (root.parentFlickable) {
hoverEnabled: root.enabled root.parentFlickable.ensureVisible(root)
}
onEntered: { }
if (rightImageSource) { }
rightImageBackground.color = rightImage.hoveredColor
} else if (leftImageSource) { Connections {
leftImageBackground.color = rightImage.hoveredColor target: rightImage
} function onFocusChanged() {
root.textOpacity = 0.8 if (rightImage.activeFocus) {
} if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
onExited: { }
if (rightImageSource) { }
rightImageBackground.color = rightImage.defaultColor }
} else if (leftImageSource) { }
leftImageBackground.color = rightImage.defaultColor
} MouseArea {
root.textOpacity = 1 anchors.fill: parent
} cursorShape: Qt.PointingHandCursor
hoverEnabled: root.enabled
onPressedChanged: {
if (rightImageSource) { onEntered: {
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor if (rightImageSource) {
} else if (leftImageSource) { rightImageBackground.color = rightImage.hoveredColor
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor } else if (leftImageSource) {
} leftImageBackground.color = rightImage.hoveredColor
root.textOpacity = 0.7 }
} root.textOpacity = 0.8
}
onClicked: {
if (clickedFunction && typeof clickedFunction === "function") { onExited: {
clickedFunction() if (rightImageSource) {
} rightImageBackground.color = rightImage.defaultColor
} } else if (leftImageSource) {
} leftImageBackground.color = rightImage.defaultColor
}
RowLayout { root.textOpacity = 1
id: content }
anchors.fill: parent
anchors.leftMargin: 16 onPressedChanged: {
anchors.rightMargin: 16 if (rightImageSource) {
anchors.topMargin: 16 rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
anchors.bottomMargin: 16 } else if (leftImageSource) {
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
Rectangle { }
id: leftImageBackground root.textOpacity = 0.7
}
visible: leftImageSource ? true : false
onClicked: {
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56 if (clickedFunction && typeof clickedFunction === "function") {
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56 clickedFunction()
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0 }
}
radius: 12 }
color: AmneziaStyle.color.transparent
RowLayout {
Behavior on color { id: content
PropertyAnimation { duration: 200 } anchors.fill: parent
} anchors.leftMargin: 16
anchors.rightMargin: 16
Image { anchors.topMargin: 16
id: leftImage anchors.bottomMargin: 16
anchors.centerIn: parent Rectangle {
source: leftImageSource id: leftImageBackground
}
} visible: leftImageSource ? true : false
ColumnLayout { Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
property real textLineHeight: 21.6 Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
property real descriptionTextLineHeight: 16 Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
property int textPixelSize: 18 radius: 12
property int descriptionTextSize: 13 color: AmneziaStyle.color.transparent
ListItemTitleType { Behavior on color {
text: root.text PropertyAnimation { duration: 200 }
color: { }
if (root.enabled) {
return root.descriptionOnTop ? root.descriptionColor : root.textColor Image {
} else { id: leftImage
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
} anchors.centerIn: parent
} source: leftImageSource
}
maximumLineCount: root.textMaximumLineCount }
elide: root.textElide
ColumnLayout {
opacity: root.textOpacity property real textLineHeight: 21.6
property real descriptionTextLineHeight: 16
Layout.fillWidth: true
property int textPixelSize: 18
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight property int descriptionTextSize: 13
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0 ListItemTitleType {
text: root.text
horizontalAlignment: Text.AlignLeft color: {
verticalAlignment: Text.AlignVCenter if (root.enabled) {
return root.descriptionOnTop ? root.descriptionColor : root.textColor
Behavior on opacity { } else {
PropertyAnimation { duration: 200 } return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
} }
} }
maximumLineCount: root.textMaximumLineCount
CaptionTextType { elide: root.textElide
id: description
opacity: root.textOpacity
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
color: { Layout.fillWidth: true
if (root.enabled) {
return root.descriptionOnTop ? root.textColor : root.descriptionColor lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
} else { font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
}
} horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
opacity: root.textOpacity
Behavior on opacity {
visible: root.descriptionText !== "" PropertyAnimation { duration: 200 }
}
Layout.fillWidth: true }
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize CaptionTextType {
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02 id: description
horizontalAlignment: Text.AlignLeft text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
verticalAlignment: Text.AlignVCenter color: {
if (root.enabled) {
Behavior on opacity { return root.descriptionOnTop ? root.textColor : root.descriptionColor
PropertyAnimation { duration: 200 } } else {
} return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
}
function replaceWithAsterisks(input) { }
return '*'.repeat(input.length)
} opacity: root.textOpacity
}
} visible: root.descriptionText !== ""
ImageButtonType { Layout.fillWidth: true
id: eyeImage
visible: buttonImageSource !== "" lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
implicitWidth: 40 font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
implicitHeight: 40
horizontalAlignment: Text.AlignLeft
hoverEnabled: true verticalAlignment: Text.AlignVCenter
image: buttonImageSource
imageColor: rightImageColor Behavior on opacity {
PropertyAnimation { duration: 200 }
Layout.alignment: Qt.AlignRight }
Rectangle { function replaceWithAsterisks(input) {
id: eyeImageBackground return '*'.repeat(input.length)
anchors.fill: parent }
radius: 12 }
color: AmneziaStyle.color.transparent }
Behavior on color { ImageButtonType {
PropertyAnimation { duration: 200 } id: eyeImage
} visible: buttonImageSource !== ""
}
implicitWidth: 40
onClicked: { implicitHeight: 40
hideDescription = !hideDescription
} hoverEnabled: true
image: buttonImageSource
Keys.onEnterPressed: { imageColor: rightImageColor
clicked()
} Layout.alignment: Qt.AlignRight
Keys.onReturnPressed: { Rectangle {
clicked() id: eyeImageBackground
} anchors.fill: parent
} radius: 12
color: AmneziaStyle.color.transparent
ImageButtonType {
id: rightImage Behavior on color {
PropertyAnimation { duration: 200 }
implicitWidth: 40 }
implicitHeight: 40 }
hoverEnabled: false onClicked: {
image: rightImageSource hideDescription = !hideDescription
imageColor: rightImageColor }
visible: rightImageSource ? true : false
Keys.onEnterPressed: {
Layout.alignment: Qt.AlignRight clicked()
}
Rectangle {
id: rightImageBackground Keys.onReturnPressed: {
anchors.fill: parent clicked()
radius: 12 }
color: AmneziaStyle.color.transparent }
Behavior on color { ImageButtonType {
PropertyAnimation { duration: 200 } id: rightImage
}
} implicitWidth: 40
onClicked: { implicitHeight: 40
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction() hoverEnabled: false
} image: rightImageSource
} imageColor: rightImageColor
} visible: rightImageSource ? true : false
}
Layout.alignment: Qt.AlignRight
Rectangle {
id: background Rectangle {
anchors.fill: root id: rightImageBackground
color: AmneziaStyle.color.transparent anchors.fill: parent
radius: 12
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent color: AmneziaStyle.color.transparent
border.width: root.activeFocus ? root.borderFocusedWidth : 0
Behavior on color {
PropertyAnimation { duration: 200 }
Behavior on color { }
PropertyAnimation { duration: 200 } }
} onClicked: {
} if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
Keys.onEnterPressed: { }
if (clickedFunction && typeof clickedFunction === "function") { }
clickedFunction() }
} }
}
Rectangle {
Keys.onReturnPressed: { id: background
if (clickedFunction && typeof clickedFunction === "function") { anchors.fill: root
clickedFunction() 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()
}
}
}
+25 -8
View File
@@ -6,16 +6,33 @@ ListView {
property bool isFocusable: 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()
}
ScrollBar.vertical: ScrollBarType {} ScrollBar.vertical: ScrollBarType {}
clip: true clip: true
reuseItems: 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" import "TextTypes"
ListViewType { ListView {
id: root id: root
property var rootWidth property var rootWidth
@@ -25,6 +25,13 @@ ListViewType {
width: rootWidth width: rootWidth
height: root.contentItem.height height: root.contentItem.height
clip: true
reuseItems: true
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
+1 -1
View File
@@ -7,5 +7,5 @@ import "../Controls2"
ScrollBar { ScrollBar {
id: root id: root
policy: ScrollBar.AsNeeded policy: parent.height >= parent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
} }
+172 -162
View File
@@ -1,162 +1,172 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Switch { Switch {
id: root id: root
property alias descriptionText: description.text property alias descriptionText: description.text
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string checkedIndicatorColor: AmneziaStyle.color.richBrown property string checkedIndicatorColor: AmneziaStyle.color.richBrown
property string defaultIndicatorColor: AmneziaStyle.color.transparent property string defaultIndicatorColor: AmneziaStyle.color.transparent
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
property bool isFocusable: true property bool isFocusable: true
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus focusPolicy: Qt.TabFocus
indicator: Rectangle { property FlickableType parentFlickable: null
id: switcher
onFocusChanged: {
anchors.right: parent.right if (root.activeFocus) {
anchors.verticalCenter: parent.verticalCenter if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
implicitWidth: 52 }
implicitHeight: 32 }
}
radius: 16
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor) indicator: Rectangle {
: root.defaultIndicatorColor id: switcher
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor) anchors.right: parent.right
: root.defaultIndicatorBorderColor) anchors.verticalCenter: parent.verticalCenter
Behavior on color { implicitWidth: 52
PropertyAnimation { duration: 200 } implicitHeight: 32
}
Behavior on border.color { radius: 16
PropertyAnimation { duration: 200 } color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
} : root.defaultIndicatorColor
Rectangle { border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
id: innerCircle : root.defaultIndicatorBorderColor)
anchors.verticalCenter: parent.verticalCenter Behavior on color {
x: root.checked ? parent.width - width - 4 : 8 PropertyAnimation { duration: 200 }
width: root.checked ? 24 : 16 }
height: root.checked ? 24 : 16 Behavior on border.color {
radius: 23 PropertyAnimation { duration: 200 }
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor) }
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
Rectangle {
Behavior on x { id: innerCircle
PropertyAnimation { duration: 200 }
} anchors.verticalCenter: parent.verticalCenter
} x: root.checked ? parent.width - width - 4 : 8
width: root.checked ? 24 : 16
Rectangle { height: root.checked ? 24 : 16
anchors.centerIn: innerCircle radius: 23
width: 40 color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
height: 40 : (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
radius: 23
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor Behavior on x {
PropertyAnimation { duration: 200 }
Behavior on color { }
PropertyAnimation { duration: 200 } }
}
} Rectangle {
} anchors.centerIn: innerCircle
width: 40
contentItem: ColumnLayout { height: 40
id: content radius: 23
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left Behavior on color {
PropertyAnimation { duration: 200 }
ListItemTitleType { }
Layout.fillWidth: true }
rightPadding: indicator.width }
text: root.text contentItem: ColumnLayout {
color: root.enabled ? root.textColor : root.textDisabledColor id: content
}
anchors.verticalCenter: parent.verticalCenter
CaptionTextType { anchors.left: parent.left
id: description
ListItemTitleType {
Layout.fillWidth: true Layout.fillWidth: true
rightPadding: indicator.width rightPadding: indicator.width
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor text: root.text
color: root.enabled ? root.textColor : root.textDisabledColor
visible: text !== "" }
}
} CaptionTextType {
id: description
MouseArea {
anchors.fill: parent Layout.fillWidth: true
cursorShape: Qt.PointingHandCursor rightPadding: indicator.width
enabled: false
} color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
Keys.onEnterPressed: event => handleSwitch(event) visible: text !== ""
Keys.onReturnPressed: event => handleSwitch(event) }
Keys.onSpacePressed: event => handleSwitch(event) }
function handleSwitch(event) { MouseArea {
if (!event.isAutoRepeat) { anchors.fill: parent
root.checked = !root.checked cursorShape: Qt.PointingHandCursor
root.checkedChanged() enabled: false
} }
event.accepted = true
} 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
}
}
+118 -135
View File
@@ -1,135 +1,118 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Style 1.0 import Style 1.0
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
MouseArea { property FlickableType parentFlickable: null
id: parentMouse onFocusChanged: {
anchors.fill: parent if (root.activeFocus) {
cursorShape: Qt.IBeamCursor if (root.parentFlickable) {
onClicked: textArea.forceActiveFocus() root.parentFlickable.ensureVisible(root)
hoverEnabled: true }
}
FlickableType { }
id: fl
interactive: false MouseArea {
id: parentMouse
anchors.top: parent.top anchors.fill: parent
anchors.bottom: parent.bottom cursorShape: Qt.IBeamCursor
contentHeight: textArea.implicitHeight onClicked: textArea.forceActiveFocus()
TextArea { hoverEnabled: true
id: textArea
FlickableType {
width: parent.width id: fl
interactive: false
topPadding: 16
leftPadding: 16 anchors.top: parent.top
anchors.topMargin: 16 anchors.bottom: parent.bottom
anchors.bottomMargin: 16 contentHeight: textArea.implicitHeight
TextArea {
property bool isFocusable: true id: textArea
Keys.onTabPressed: { width: parent.width
FocusController.nextKeyTabItem()
} topPadding: 16
leftPadding: 16
Keys.onBacktabPressed: { anchors.topMargin: 16
FocusController.previousKeyTabItem() anchors.bottomMargin: 16
}
color: AmneziaStyle.color.paleGray
Keys.onUpPressed: { selectionColor: AmneziaStyle.color.richBrown
FocusController.nextKeyUpItem() selectedTextColor: AmneziaStyle.color.paleGray
} placeholderTextColor: AmneziaStyle.color.mutedGray
Keys.onDownPressed: { font.pixelSize: 16
FocusController.nextKeyDownItem() font.weight: Font.Medium
} font.family: "PT Root UI VF"
Keys.onLeftPressed: { placeholderText: root.placeholderText
FocusController.nextKeyLeftItem() text: root.text
}
onCursorVisibleChanged: {
Keys.onRightPressed: { if (textArea.cursorVisible) {
FocusController.nextKeyRightItem() fl.interactive = true
} } else {
fl.interactive = false
color: AmneziaStyle.color.paleGray }
selectionColor: AmneziaStyle.color.richBrown }
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray wrapMode: Text.Wrap
font.pixelSize: 16 MouseArea {
font.weight: Font.Medium id: textAreaMouse
font.family: "PT Root UI VF" anchors.fill: parent
acceptedButtons: Qt.RightButton
placeholderText: root.placeholderText hoverEnabled: true
text: root.text onClicked: {
fl.interactive = true
onCursorVisibleChanged: { contextMenu.open()
if (textArea.cursorVisible) { }
fl.interactive = true }
} else {
fl.interactive = false onFocusChanged: {
} root.border.color = getBorderColor(borderNormalColor)
} }
wrapMode: Text.Wrap ContextMenuType {
id: contextMenu
MouseArea { textObj: textArea
id: textAreaMouse }
anchors.fill: parent }
acceptedButtons: Qt.RightButton }
hoverEnabled: true
onClicked: { onPressed: {
fl.interactive = true root.border.color = getBorderColor(borderFocusedColor)
contextMenu.open() }
}
} onExited: {
root.border.color = getBorderColor(borderNormalColor)
onFocusChanged: { }
root.border.color = getBorderColor(borderNormalColor)
} onEntered: {
root.border.color = getBorderColor(borderHoveredColor)
ContextMenuType { }
id: contextMenu }
textObj: textArea
}
} function getBorderColor(noneFocusedColor) {
} return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
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
}
}
+180 -171
View File
@@ -1,171 +1,180 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property string headerText property string headerText
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string firstButtonImage property string firstButtonImage
property string secondButtonImage property string secondButtonImage
property var firstButtonClickedFunc property var firstButtonClickedFunc
property var secondButtonClickedFunc property var secondButtonClickedFunc
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
MouseArea { property FlickableType parentFlickable: null
id: parentMouse onFocusChanged: {
anchors.fill: parent if (root.activeFocus) {
cursorShape: Qt.IBeamCursor if (root.parentFlickable) {
onClicked: textArea.forceActiveFocus() root.parentFlickable.ensureVisible(root)
hoverEnabled: true }
}
ColumnLayout { }
anchors.fill: parent
anchors.margins: 16 MouseArea {
spacing: 0 id: parentMouse
anchors.fill: parent
LabelTextType { cursorShape: Qt.IBeamCursor
Layout.fillWidth: true onClicked: textArea.forceActiveFocus()
text: root.headerText hoverEnabled: true
}
ColumnLayout {
TextArea { anchors.fill: parent
id: textArea anchors.margins: 16
spacing: 0
Layout.fillWidth: true
Layout.fillHeight: true LabelTextType {
Layout.fillWidth: true
leftPadding: 0 text: root.headerText
Layout.bottomMargin: 16 }
color: AmneziaStyle.color.paleGray TextArea {
selectionColor: AmneziaStyle.color.richBrown id: textArea
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray Layout.fillWidth: true
Layout.fillHeight: true
font.pixelSize: 16
font.weight: Font.Medium leftPadding: 0
font.family: "PT Root UI VF" Layout.bottomMargin: 16
placeholderText: root.placeholderText color: AmneziaStyle.color.paleGray
text: root.text selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
onCursorVisibleChanged: { placeholderTextColor: AmneziaStyle.color.mutedGray
if (textArea.cursorVisible) {
fl.interactive = true font.pixelSize: 16
} else { font.weight: Font.Medium
fl.interactive = false font.family: "PT Root UI VF"
}
} placeholderText: root.placeholderText
text: root.text
wrapMode: Text.Wrap
onCursorVisibleChanged: {
MouseArea { if (textArea.cursorVisible) {
id: textAreaMouse fl.interactive = true
anchors.fill: parent } else {
acceptedButtons: Qt.RightButton fl.interactive = false
hoverEnabled: true }
onClicked: { }
fl.interactive = true
contextMenu.open() wrapMode: Text.Wrap
}
} MouseArea {
id: textAreaMouse
onFocusChanged: { anchors.fill: parent
root.border.color = getBorderColor(borderNormalColor) acceptedButtons: Qt.RightButton
} hoverEnabled: true
onClicked: {
ContextMenuType { fl.interactive = true
id: contextMenu contextMenu.open()
textObj: textArea }
} }
}
onFocusChanged: {
RowLayout { root.border.color = getBorderColor(borderNormalColor)
Layout.fillWidth: true }
Layout.leftMargin: -8
spacing: 0 ContextMenuType {
ImageButtonType { id: contextMenu
id: firstButton textObj: textArea
visible: root.firstButtonImage !== "" }
}
imageColor: AmneziaStyle.color.paleGray
RowLayout {
image: root.firstButtonImage Layout.fillWidth: true
onClicked: function() { Layout.leftMargin: -8
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") { spacing: 0
root.firstButtonClickedFunc() ImageButtonType {
} id: firstButton
} visible: root.firstButtonImage !== ""
}
imageColor: AmneziaStyle.color.paleGray
ImageButtonType {
id: secondButton image: root.firstButtonImage
visible: root.secondButtonImage !== "" onClicked: function() {
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
imageColor: AmneziaStyle.color.paleGray root.firstButtonClickedFunc()
}
image: root.secondButtonImage }
onClicked: function() { }
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
root.secondButtonClickedFunc() ImageButtonType {
} id: secondButton
} visible: root.secondButtonImage !== ""
}
imageColor: AmneziaStyle.color.paleGray
Item {
Layout.fillWidth: true image: root.secondButtonImage
} onClicked: function() {
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
ImageButtonType { root.secondButtonClickedFunc()
id: resetButton }
imageColor: AmneziaStyle.color.paleGray }
}
visible: root.textAreaText !== ""
image: "qrc:/images/controls/close.svg" Item {
Layout.fillWidth: true
onClicked: function() { }
root.textAreaText = ""
textArea.focus = true ImageButtonType {
} id: resetButton
} imageColor: AmneziaStyle.color.paleGray
}
} visible: root.textAreaText !== ""
image: "qrc:/images/controls/close.svg"
onPressed: {
root.border.color = getBorderColor(borderFocusedColor) onClicked: function() {
} root.textAreaText = ""
textArea.focus = true
onExited: { }
root.border.color = getBorderColor(borderNormalColor) }
} }
}
onEntered: {
root.border.color = getBorderColor(borderHoveredColor) onPressed: {
} root.border.color = getBorderColor(borderFocusedColor)
} }
onExited: {
function getBorderColor(noneFocusedColor) { root.border.color = getBorderColor(borderNormalColor)
return textArea.focus ? root.borderFocusedColor : noneFocusedColor }
}
} onEntered: {
root.border.color = getBorderColor(borderHoveredColor)
}
}
function getBorderColor(noneFocusedColor) {
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
}
@@ -1,220 +1,233 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
property string headerText property string headerText
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
property string headerTextColor: AmneziaStyle.color.mutedGray property string headerTextColor: AmneziaStyle.color.mutedGray
property alias errorText: errorField.text property alias errorText: errorField.text
property bool checkEmptyText: false property bool checkEmptyText: false
property bool rightButtonClickedOnEnter: false property bool rightButtonClickedOnEnter: false
property string buttonText property string buttonText
property string buttonImageSource property string buttonImageSource
property var clickedFunc property var clickedFunc
property alias textField: textField property alias textField: textField
property string textFieldTextColor: AmneziaStyle.color.paleGray property string textFieldTextColor: AmneziaStyle.color.paleGray
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
property bool textFieldEditable: true property bool textFieldEditable: true
property string borderColor: AmneziaStyle.color.slateGray property string borderColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string backgroundColor: AmneziaStyle.color.onyxBlack property string backgroundColor: AmneziaStyle.color.onyxBlack
property string backgroundDisabledColor: AmneziaStyle.color.transparent property string backgroundDisabledColor: AmneziaStyle.color.transparent
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
ColumnLayout { property FlickableType parentFlickable
id: content
anchors.fill: parent Connections {
target: textField
Rectangle { function onFocusChanged() {
id: backgroud if (textField.activeFocus) {
Layout.fillWidth: true if (root.parentFlickable) {
Layout.preferredHeight: input.implicitHeight root.parentFlickable.ensureVisible(root)
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor }
radius: 16 }
border.color: getBackgroundBorderColor(root.borderColor) }
border.width: 1 }
Behavior on border.color { ColumnLayout {
PropertyAnimation { duration: 200 } id: content
} anchors.fill: parent
RowLayout { Rectangle {
id: input id: backgroud
anchors.fill: backgroud Layout.fillWidth: true
ColumnLayout { Layout.preferredHeight: input.implicitHeight
Layout.margins: 16 color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
LabelTextType { radius: 16
text: root.headerText border.color: getBackgroundBorderColor(root.borderColor)
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor border.width: 1
visible: text !== "" Behavior on border.color {
PropertyAnimation { duration: 200 }
Layout.fillWidth: true }
}
RowLayout {
TextField { id: input
id: textField anchors.fill: backgroud
ColumnLayout {
property bool isFocusable: true Layout.margins: 16
LabelTextType {
Keys.onTabPressed: { text: root.headerText
FocusController.nextKeyTabItem() color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
}
visible: text !== ""
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() Layout.fillWidth: true
} }
enabled: root.textFieldEditable TextField {
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor id: textField
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText property bool isFocusable: true
placeholderTextColor: AmneziaStyle.color.charcoalGray Keys.onTabPressed: {
FocusController.nextKeyTabItem()
selectionColor: AmneziaStyle.color.richBrown }
selectedTextColor: AmneziaStyle.color.paleGray
Keys.onBacktabPressed: {
font.pixelSize: 16 FocusController.previousKeyTabItem()
font.weight: 400 }
font.family: "PT Root UI VF"
enabled: root.textFieldEditable
height: 24 color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
Layout.fillWidth: true
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
topPadding: 0
rightPadding: 0 placeholderTextColor: AmneziaStyle.color.charcoalGray
leftPadding: 0
bottomPadding: 0 selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
background: Rectangle {
anchors.fill: parent font.pixelSize: 16
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor font.weight: 400
} font.family: "PT Root UI VF"
onTextChanged: { height: 24
root.errorText = "" Layout.fillWidth: true
}
topPadding: 0
onActiveFocusChanged: { rightPadding: 0
if (root.checkEmptyText && text === "") { leftPadding: 0
root.errorText = qsTr("The field can't be empty") bottomPadding: 0
}
} background: Rectangle {
anchors.fill: parent
MouseArea { color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
anchors.fill: parent }
acceptedButtons: Qt.RightButton
onClicked: contextMenu.open() onTextChanged: {
enabled: true root.errorText = ""
} }
ContextMenuType { onActiveFocusChanged: {
id: contextMenu if (root.checkEmptyText && text === "") {
textObj: textField root.errorText = qsTr("The field can't be empty")
} }
}
onFocusChanged: {
backgroud.border.color = getBackgroundBorderColor(root.borderColor) MouseArea {
} anchors.fill: parent
} acceptedButtons: Qt.RightButton
} onClicked: contextMenu.open()
} enabled: true
} }
SmallTextType { ContextMenuType {
id: errorField id: contextMenu
textObj: textField
text: root.errorText }
visible: root.errorText !== ""
color: AmneziaStyle.color.vibrantRed onFocusChanged: {
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
Layout.fillWidth: true }
} }
} }
}
MouseArea { }
anchors.fill: root
cursorShape: Qt.IBeamCursor SmallTextType {
id: errorField
hoverEnabled: true
text: root.errorText
onPressed: function(mouse) { visible: root.errorText !== ""
textField.forceActiveFocus() color: AmneziaStyle.color.vibrantRed
mouse.accepted = false
Layout.fillWidth: true
backgroud.border.color = getBackgroundBorderColor(root.borderColor) }
} }
onEntered: { MouseArea {
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor) anchors.fill: root
} cursorShape: Qt.IBeamCursor
hoverEnabled: true
onExited: {
backgroud.border.color = getBackgroundBorderColor(root.borderColor) onPressed: function(mouse) {
} textField.forceActiveFocus()
} mouse.accepted = false
BasicButtonType { backgroud.border.color = getBackgroundBorderColor(root.borderColor)
visible: (root.buttonText !== "") || (root.buttonImageSource !== "") }
focusPolicy: Qt.NoFocus onEntered: {
text: root.buttonText backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
leftImageSource: root.buttonImageSource }
anchors.top: content.top
anchors.bottom: content.bottom onExited: {
anchors.right: content.right backgroud.border.color = getBackgroundBorderColor(root.borderColor)
}
height: content.implicitHeight }
width: content.implicitHeight
squareLeftSide: true BasicButtonType {
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
clickedFunc: function() {
if (root.clickedFunc && typeof root.clickedFunc === "function") { focusPolicy: Qt.NoFocus
root.clickedFunc() text: root.buttonText
} leftImageSource: root.buttonImageSource
}
} anchors.top: content.top
anchors.bottom: content.bottom
function getBackgroundBorderColor(noneFocusedColor) { anchors.right: content.right
return textField.focus ? root.borderFocusedColor : noneFocusedColor
} height: content.implicitHeight
width: content.implicitHeight
Keys.onEnterPressed: { squareLeftSide: true
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc() clickedFunc: function() {
} if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
// if (KeyNavigation.tab) { }
// KeyNavigation.tab.forceActiveFocus(); }
// } }
}
function getBackgroundBorderColor(noneFocusedColor) {
Keys.onReturnPressed: { return textField.focus ? root.borderFocusedColor : noneFocusedColor
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") { }
clickedFunc()
} Keys.onEnterPressed: {
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
// if (KeyNavigation.tab) { clickedFunc()
// KeyNavigation.tab.forceActiveFocus(); }
// }
} // if (KeyNavigation.tab) {
} // KeyNavigation.tab.forceActiveFocus();
// }
}
Keys.onReturnPressed: {
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
}
// if (KeyNavigation.tab) {
// KeyNavigation.tab.forceActiveFocus();
// }
}
}
+48 -38
View File
@@ -20,9 +20,7 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
sourceModel: ServersModel sourceModel: ServersModel
filters: [ filters: [
ValueFilter { ValueFilter {
roleName: "isCurrentlyProcessed" roleName: "isCurrentlyProcessed"
@@ -31,55 +29,67 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
spacing: 16 Column {
id: content
model: proxyServersModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { spacing: 16
width: listView.width
BaseHeaderType { Repeater {
Layout.fillWidth: true model: proxyServersModel
Layout.topMargin: 20 delegate: Item {
Layout.leftMargin: 16 implicitWidth: parent.width
Layout.rightMargin: 16 implicitHeight: delegateContent.implicitHeight
headerText: qsTr("Removing services from %1").arg(name) ColumnLayout {
} id: delegateContent
ProgressBarType { anchors.fill: parent
id: progressBar anchors.rightMargin: 16
anchors.leftMargin: 16
Layout.fillWidth: true BaseHeaderType {
Layout.topMargin: 32 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.topMargin: 20
Layout.rightMargin: 16
Timer { headerText: qsTr("Removing services from %1").arg(name)
id: timer }
interval: 300 ProgressBarType {
repeat: true id: progressBar
running: true
onTriggered: { Layout.fillWidth: true
progressBar.value += 0.003 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")
}
} }
} }
} }
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Usually it takes no more than 5 minutes")
}
} }
} }
} }
+13 -3
View File
@@ -25,17 +25,23 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
@@ -44,14 +50,16 @@ PageType {
} }
} }
model: 1 // fake model to force the ListView to be created without a model model: 1
clip: true
spacing: 16 spacing: 16
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: passwordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@@ -79,6 +87,8 @@ PageType {
width: listView.width width: listView.width
SwitcherType { SwitcherType {
id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
+6 -9
View File
@@ -101,8 +101,8 @@ PageType {
visible: isLoggingEnabled ? true : false visible: isLoggingEnabled ? true : false
text: qsTr("Logging enabled") text: qsTr("Logging enabled")
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: loggingButton.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: loggingButton.clicked()
onClicked: { onClicked: {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
@@ -147,8 +147,8 @@ PageType {
leftImageColor: "" leftImageColor: ""
rightImageSource: "qrc:/images/controls/chevron-down.svg" rightImageSource: "qrc:/images/controls/chevron-down.svg"
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: splitTunnelingButton.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: splitTunnelingButton.clicked()
onClicked: { onClicked: {
homeSplitTunnelingDrawer.openTriggered() homeSplitTunnelingDrawer.openTriggered()
@@ -276,8 +276,8 @@ PageType {
topPadding: 4 topPadding: 4
bottomPadding: 3 bottomPadding: 3
Keys.onEnterPressed: this.clicked() Keys.onEnterPressed: collapsedButtonChevron.clicked()
Keys.onReturnPressed: this.clicked() Keys.onReturnPressed: collapsedButtonChevron.clicked()
onClicked: { onClicked: {
if (drawer.isCollapsedStateActive()) { if (drawer.isCollapsedStateActive()) {
@@ -320,9 +320,6 @@ PageType {
rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : "" rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : ""
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
onClicked: { onClicked: {
ServersModel.processedIndex = ServersModel.defaultIndex ServersModel.processedIndex = ServersModel.defaultIndex
@@ -16,397 +16,349 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { ListView {
id: listView id: listview
anchors.top: backButton.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: saveButton.top anchors.bottom: saveButton.top
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout { width: parent.width
width: listView.width
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") 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()
} }
model: AwgConfigModel model: AwgConfigModel
delegate: ColumnLayout { delegate: Item {
width: listView.width id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" && property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" && junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" && junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" junkPacketCountTextField.errorText === ""
spacing: 0 ColumnLayout {
id: col
TextFieldWithHeaderType { anchors.top: parent.top
id: mtuTextField anchors.left: parent.left
anchors.right: parent.right
Layout.fillWidth: true anchors.leftMargin: 16
Layout.topMargin: 40 anchors.rightMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU") spacing: 0
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: { BaseHeaderType {
if (textField.text !== clientMtu) { Layout.fillWidth: true
clientMtu = textField.text
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
}
} }
} }
checkEmptyText: true
}
AwgTextField { AwgTextField {
id: junkPacketCountTextField id: specialJunk1TextField
headerText: qsTr("I1 - First special junk packet")
textField.text: clientSpecialJunk1
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk1) {
clientSpecialJunk1 = textField.text
headerText: "Jc - Junk packet count" }
textField.text: clientJunkPacketCount
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketCount) {
clientJunkPacketCount = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: junkPacketMinSizeTextField id: specialJunk2TextField
headerText: qsTr("I2 - Second special junk packet")
textField.text: clientSpecialJunk2
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk2) {
clientSpecialJunk2 = textField.text
headerText: "Jmin - Junk packet minimum size" }
textField.text: clientJunkPacketMinSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: junkPacketMaxSizeTextField id: specialJunk3TextField
headerText: qsTr("I3 - Third special junk packet")
textField.text: clientSpecialJunk3
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk3) {
clientSpecialJunk3 = textField.text
headerText: "Jmax - Junk packet maximum size" }
textField.text: clientJunkPacketMaxSize
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk1TextField id: specialJunk4TextField
headerText: qsTr("I4 - Fourth special junk packet")
textField.text: clientSpecialJunk4
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk4) {
clientSpecialJunk4 = textField.text
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 { AwgTextField {
id: specialJunk2TextField id: specialJunk5TextField
headerText: qsTr("I5 - Fifth special junk packet")
textField.text: clientSpecialJunk5
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialJunk5 ) {
clientSpecialJunk5 = textField.text
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 { AwgTextField {
id: specialJunk3TextField id: controlledJunk1TextField
headerText: qsTr("J1 - First controlled junk packet")
textField.text: clientControlledJunk1
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientControlledJunk1) {
clientControlledJunk1 = textField.text
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 { AwgTextField {
id: specialJunk4TextField id: controlledJunk2TextField
headerText: qsTr("J2 - Second controlled junk packet")
Layout.leftMargin: 16 textField.text: clientControlledJunk2
Layout.rightMargin: 16 textField.validator: null
checkEmptyText: false
headerText: qsTr("I4 - Fourth special junk packet") textField.onEditingFinished: {
textField.text: clientSpecialJunk4 if (textField.text !== clientControlledJunk2) {
textField.validator: null clientControlledJunk2 = textField.text
checkEmptyText: false }
textField.onEditingFinished: {
if (textField.text !== clientSpecialJunk4) {
clientSpecialJunk4 = textField.text
} }
} }
}
AwgTextField { AwgTextField {
id: specialJunk5TextField id: controlledJunk3TextField
headerText: qsTr("J3 - Third controlled junk packet")
textField.text: clientControlledJunk3
textField.validator: null
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientControlledJunk3) {
clientControlledJunk3 = textField.text
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 { AwgTextField {
id: controlledJunk1TextField id: iTimeTextField
headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false
Layout.leftMargin: 16 textField.onEditingFinished: {
Layout.rightMargin: 16 if (textField.text !== clientSpecialHandshakeTimeout) {
clientSpecialHandshakeTimeout = textField.text
headerText: qsTr("J1 - First controlled junk packet") }
textField.text: clientControlledJunk1
textField.validator: null
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientControlledJunk1) {
clientControlledJunk1 = textField.text
} }
} }
}
AwgTextField { Header2TextType {
id: controlledJunk2TextField Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16 text: qsTr("Server settings")
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 { AwgTextField {
id: controlledJunk3TextField id: portTextField
enabled: false
Layout.leftMargin: 16 headerText: qsTr("Port")
Layout.rightMargin: 16 textField.text: port
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 { AwgTextField {
id: iTimeTextField id: initPacketJunkSizeTextField
enabled: false
Layout.leftMargin: 16 headerText: "S1 - Init packet junk size"
Layout.rightMargin: 16 textField.text: serverInitPacketJunkSize
headerText: qsTr("Itime - Special handshake timeout")
textField.text: clientSpecialHandshakeTimeout
checkEmptyText: false
textField.onEditingFinished: {
if (textField.text !== clientSpecialHandshakeTimeout) {
clientSpecialHandshakeTimeout = textField.text
}
} }
}
Header2TextType { AwgTextField {
Layout.fillWidth: true id: responsePacketJunkSizeTextField
Layout.topMargin: 16 enabled: false
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Server settings") headerText: "S2 - Response packet junk size"
} textField.text: serverResponsePacketJunkSize
}
AwgTextField { // AwgTextField {
id: portTextField // id: cookieReplyPacketJunkSizeTextField
// enabled: false
Layout.leftMargin: 16 // headerText: "S3 - Cookie Reply packet junk size"
Layout.rightMargin: 16 // textField.text: serverCookieReplyPacketJunkSize
// }
enabled: false // AwgTextField {
// id: transportPacketJunkSizeTextField
// enabled: false
headerText: qsTr("Port") // headerText: "S4 - Transport packet junk size"
textField.text: port // textField.text: serverTransportPacketJunkSize
} // }
AwgTextField { AwgTextField {
id: initPacketJunkSizeTextField id: initPacketMagicHeaderTextField
enabled: false
Layout.leftMargin: 16 headerText: "H1 - Init packet magic header"
Layout.rightMargin: 16 textField.text: serverInitPacketMagicHeader
}
enabled: false AwgTextField {
id: responsePacketMagicHeaderTextField
enabled: false
headerText: "S1 - Init packet junk size" headerText: "H2 - Response packet magic header"
textField.text: serverInitPacketJunkSize textField.text: serverResponsePacketMagicHeader
} }
AwgTextField { AwgTextField {
id: responsePacketJunkSizeTextField id: underloadPacketMagicHeaderTextField
enabled: false
Layout.leftMargin: 16 headerText: "H3 - Underload packet magic header"
Layout.rightMargin: 16 textField.text: serverUnderloadPacketMagicHeader
}
enabled: false AwgTextField {
id: transportPacketMagicHeaderTextField
enabled: false
headerText: "S2 - Response packet junk size" headerText: "H4 - Transport packet magic header"
textField.text: serverResponsePacketJunkSize textField.text: serverTransportPacketMagicHeader
} }
// 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
} }
} }
} }
@@ -423,17 +375,18 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
enabled: listView.currentItem.isSaveButtonEnabled enabled: listview.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
onActiveFocusChanged: { onActiveFocusChanged: {
if(activeFocus) { if(activeFocus) {
listView.positionViewAtEnd() listview.positionViewAtEnd()
} }
} }
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed") var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@@ -448,9 +401,11 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig()) InstallController.updateContainer(AwgConfigModel.getConfig())
} }
var noButtonFunction = function() {
var noButtonFunction = function() {} if (!GC.isMobile()) {
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
+286 -295
View File
@@ -19,343 +19,334 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { ListView {
id: listView id: listview
anchors.top: backButton.bottom property bool isFocusable: true
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right 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
model: AwgConfigModel model: AwgConfigModel
delegate: ColumnLayout { delegate: Item {
id: delegateItem id: delegateItem
implicitWidth: listview.width
width: listView.width implicitHeight: col.implicitHeight
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
spacing: 0 ColumnLayout {
id: col
BaseHeaderType { anchors.top: parent.top
Layout.fillWidth: true anchors.left: parent.left
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") anchors.leftMargin: 16
} anchors.rightMargin: 16
TextFieldWithHeaderType { spacing: 0
id: vpnAddressSubnetTextField
Layout.fillWidth: true BaseHeaderType {
Layout.topMargin: 40 Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled headerText: qsTr("AmneziaWG settings")
}
headerText: qsTr("VPN address subnet") TextFieldWithHeaderType {
textField.text: subnetAddress id: vpnAddressSubnetTextField
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== subnetAddress) { Layout.topMargin: 40
subnetAddress = textField.text
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
}
} }
} }
checkEmptyText: true AwgTextField {
} id: junkPacketMinSizeTextField
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
TextFieldWithHeaderType { textField.onEditingFinished: {
id: portTextField if (textField.text !== serverJunkPacketMinSize) {
Layout.fillWidth: true serverJunkPacketMinSize = textField.text
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 AwgTextField {
} id: junkPacketMaxSizeTextField
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
AwgTextField { textField.onEditingFinished: {
id: junkPacketCountTextField if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
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()
} }
} }
clickedFunc: function() { AwgTextField {
if (delegateItem.isEnabled) { id: initPacketJunkSizeTextField
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, headerText: qsTr("S1 - Init packet junk size")
transportPacketMagicHeaderTextField.textField.text, textField.text: serverInitPacketJunkSize
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) { textField.onEditingFinished: {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique")) if (textField.text !== serverInitPacketJunkSize) {
return serverInitPacketJunkSize = textField.text
}
}
}
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()
}
}
clickedFunc: function() {
forceActiveFocus()
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
// }
} }
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), var headerText = qsTr("Save settings?")
parseInt(responsePacketJunkSizeTextField.textField.text))) { var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) var yesButtonText = qsTr("Continue")
return 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())
} }
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), var noButtonFunction = function() {
// parseInt(responsePacketJunkSizeTextField.textField.text), if (!GC.isMobile()) {
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text), saveRestartButton.forceActiveFocus()
// 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 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
} }
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)
} }
} }
} }
+150 -130
View File
@@ -16,191 +16,211 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.implicitHeight
anchors.right: parent.right
property int selectedIndex: 0 Column {
id: content
enabled: ServersModel.isProcessedServerHasWriteAccess() anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
header: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
BaseHeaderType { ListView {
Layout.fillWidth: true id: listview
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Cloak settings") property int selectedIndex: 0
}
}
model: CloakConfigModel width: parent.width
height: listview.contentItem.height
delegate: ColumnLayout { clip: true
width: listView.width reuseItems: true
property alias trafficFromField: trafficFromField model: CloakConfigModel
spacing: 0 delegate: Item {
id: delegateItem
TextFieldWithHeaderType { property alias trafficFromField: trafficFromField
id: trafficFromField property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.fillWidth: true implicitWidth: listview.width
Layout.topMargin: 32 implicitHeight: col.implicitHeight
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Disguised as traffic from") ColumnLayout {
textField.text: site id: col
textField.onEditingFinished: { anchors.top: parent.top
if (textField.text !== site) { anchors.left: parent.left
var tmpText = textField.text anchors.right: parent.right
tmpText = tmpText.toLocaleLowerCase()
var indexHttps = tmpText.indexOf("https://") anchors.leftMargin: 16
if (indexHttps === 0) { anchors.rightMargin: 16
tmpText = textField.text.substring(8)
} else { spacing: 0
site = textField.text
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("Cloak settings")
} }
}
}
checkEmptyText: true TextFieldWithHeaderType {
} id: trafficFromField
TextFieldWithHeaderType { Layout.fillWidth: true
id: portTextField Layout.topMargin: 32
Layout.fillWidth: true enabled: delegateItem.isEnabled
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Port") headerText: qsTr("Disguised as traffic from")
textField.text: port textField.text: site
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { textField.onEditingFinished: {
if (textField.text !== port) { if (textField.text !== site) {
port = textField.text var tmpText = textField.text
} tmpText = tmpText.toLocaleLowerCase()
}
checkEmptyText: true var indexHttps = tmpText.indexOf("https://")
} if (indexHttps === 0) {
tmpText = textField.text.substring(8)
} else {
site = textField.text
}
}
}
DropDownType { checkEmptyText: true
id: cipherDropDown }
Layout.fillWidth: true TextFieldWithHeaderType {
Layout.topMargin: 16 id: portTextField
Layout.leftMargin: 16
Layout.rightMargin: 16
descriptionText: qsTr("Cipher") Layout.fillWidth: true
headerText: qsTr("Cipher") Layout.topMargin: 16
drawerParent: root enabled: delegateItem.isEnabled
listView: ListViewWithRadioButtonType { headerText: qsTr("Port")
id: cipherListView textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
rootWidth: root.width textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
model: ListModel { checkEmptyText: true
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() { DropDownType {
cipherDropDown.text = selectedText id: cipherDropDown
cipher = cipherDropDown.text Layout.fillWidth: true
cipherDropDown.closeTriggered() Layout.topMargin: 16
}
Component.onCompleted: { enabled: delegateItem.isEnabled
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) { descriptionText: qsTr("Cipher")
if (cipherListView.model.get(i).name === cipherDropDown.text) { headerText: qsTr("Cipher")
selectedIndex = i
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 { BasicButtonType {
id: saveButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: trafficFromField.errorText === "" && enabled: trafficFromField.errorText === "" &&
portTextField.errorText === "" portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
var headerText = qsTr("Save settings?") 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 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 yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return return
} }
PageController.goToPage(PageEnum.PageSetupWizardInstalling) PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(CloakConfigModel.getConfig()) InstallController.updateContainer(CloakConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
saveButton.forceActiveFocus() saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
@@ -17,413 +17,427 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: { BackButtonType {
if(backButton.enabled && backButton.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
header: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: header
Layout.fillWidth: true ListView {
Layout.rightMargin: 16 id: listview
Layout.leftMargin: 16
headerText: qsTr("OpenVPN Settings") width: parent.width
} height: listview.contentItem.height
}
model: OpenVpnConfigModel clip: true
interactive: false
delegate: ColumnLayout { model: OpenVpnConfigModel
width: listView.width
spacing: 0 delegate: Item {
id: delegateItem
TextFieldWithHeaderType { property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
id: vpnAddressSubnetTextField property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.fillWidth: true implicitWidth: listview.width
Layout.topMargin: 32 implicitHeight: col.implicitHeight
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled ColumnLayout {
id: col
headerText: qsTr("VPN address subnet") anchors.top: parent.top
textField.text: subnetAddress anchors.left: parent.left
anchors.right: parent.right
textField.onEditingFinished: { anchors.leftMargin: 16
if (textField.text !== subnetAddress) { anchors.rightMargin: 16
subnetAddress = textField.text
}
}
checkEmptyText: true spacing: 0
}
ParagraphTextType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 headerText: qsTr("OpenVPN settings")
Layout.leftMargin: 16 }
Layout.rightMargin: 16
text: qsTr("Network protocol") TextFieldWithHeaderType {
} id: vpnAddressSubnetTextField
TransportProtoSelector { Layout.fillWidth: true
id: transportProtoSelector Layout.topMargin: 32
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
rootWidth: root.width enabled: delegateItem.isEnabled
enabled: isTransportProtoEditable headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
currentIndex: { parentFlickable: fl
return transportProto === "tcp" ? 1 : 0
}
onCurrentIndexChanged: { textField.onEditingFinished: {
if (transportProto === "tcp" && currentIndex === 0) { if (textField.text !== subnetAddress) {
transportProto = "udp" subnetAddress = textField.text
} else if (transportProto === "udp" && currentIndex === 1) { }
transportProto = "tcp" }
}
}
}
TextFieldWithHeaderType { checkEmptyText: true
id: portTextField }
Layout.fillWidth: true ParagraphTextType {
Layout.topMargin: 40 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.topMargin: 32
Layout.rightMargin: 16
enabled: listView.enabled text: qsTr("Network protocol")
}
headerText: qsTr("Port") TransportProtoSelector {
textField.text: port id: transportProtoSelector
textField.maximumLength: 5 Layout.fillWidth: true
textField.validator: IntValidator { bottom: 1; top: 65535 } Layout.topMargin: 16
rootWidth: root.width
textField.onEditingFinished: { enabled: isTransportProtoEditable
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true currentIndex: {
} return transportProto === "tcp" ? 1 : 0
}
SwitcherType { onCurrentIndexChanged: {
id: autoNegotiateEncryprionSwitcher if (transportProto === "tcp" && currentIndex === 0) {
transportProto = "udp"
Layout.fillWidth: true } else if (transportProto === "udp" && currentIndex === 1) {
Layout.topMargin: 24 transportProto = "tcp"
Layout.leftMargin: 16 }
Layout.rightMargin: 16
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
onCheckedChanged: {
if (checked !== autoNegotiateEncryprion) {
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
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
} }
} }
}
}
}
DropDownType { TextFieldWithHeaderType {
id: cipherDropDown id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: !autoNegotiateEncryprionSwitcher.checked Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
descriptionText: qsTr("Cipher") enabled: delegateItem.isEnabled
headerText: qsTr("Cipher")
drawerParent: root headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
listView: ListViewWithRadioButtonType { textField.onEditingFinished: {
id: cipherListView if (textField.text !== port) {
port = textField.text
}
}
rootWidth: root.width checkEmptyText: true
}
model: ListModel { SwitcherType {
ListElement { name : qsTr("AES-256-GCM") } id: autoNegotiateEncryprionSwitcher
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") }
}
clickedFunction: function() { Layout.fillWidth: true
cipherDropDown.text = selectedText Layout.topMargin: 24
cipher = cipherDropDown.text parentFlickable: fl
cipherDropDown.closeTriggered()
}
Component.onCompleted: { text: qsTr("Auto-negotiate encryption")
cipherDropDown.text = cipher checked: autoNegotiateEncryprion
for (var i = 0; i < cipherListView.model.count; i++) { onCheckedChanged: {
if (cipherListView.model.get(i).name === cipherDropDown.text) { if (checked !== autoNegotiateEncryprion) {
currentIndex = i autoNegotiateEncryprion = checked
}
} }
} }
}
}
}
Rectangle { DropDownType {
id: contentRect id: hashDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.preferredHeight: checkboxLayout.implicitHeight enabled: !autoNegotiateEncryprionSwitcher.checked
color: AmneziaStyle.color.onyxBlack
radius: 16
ColumnLayout { descriptionText: qsTr("Hash")
id: checkboxLayout headerText: qsTr("Hash")
anchors.fill: parent drawerParent: root
CheckBoxType { listView: ListViewWithRadioButtonType {
id: tlsAuthCheckBox id: hashListView
Layout.fillWidth: true
text: qsTr("TLS auth") rootWidth: root.width
checked: tlsAuth
onCheckedChanged: { model: ListModel {
if (checked !== tlsAuth) { ListElement { name : qsTr("SHA512") }
console.log("tlsAuth changed to: " + checked) ListElement { name : qsTr("SHA384") }
tlsAuth = checked 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
}
}
}
} }
} }
}
DividerType {} DropDownType {
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
CheckBoxType { enabled: !autoNegotiateEncryprionSwitcher.checked
id: blockDnsCheckBox
Layout.fillWidth: true
text: qsTr("Block DNS requests outside of VPN") descriptionText: qsTr("Cipher")
checked: blockDns headerText: qsTr("Cipher")
onCheckedChanged: { drawerParent: root
if (checked !== blockDns) {
blockDns = checked listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
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") }
}
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
}
}
}
} }
} }
}
}
}
SwitcherType { Rectangle {
id: additionalClientCommandsSwitcher id: contentRect
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.preferredHeight: checkboxLayout.implicitHeight
Layout.rightMargin: 16 color: AmneziaStyle.color.onyxBlack
radius: 16
checked: additionalClientCommands !== "" Connections {
target: tlsAuthCheckBox
enabled: !GC.isMobile()
text: qsTr("Additional client configuration commands") function onFocusChanged() {
if (tlsAuthCheckBox.activeFocus) {
fl.ensureVisible(contentRect)
}
}
}
onCheckedChanged: { ColumnLayout {
if (!checked) { id: checkboxLayout
additionalClientCommands = ""
}
}
}
TextAreaType { anchors.fill: parent
id: additionalClientCommandsTextArea CheckBoxType {
Layout.fillWidth: true id: tlsAuthCheckBox
Layout.topMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalClientCommandsSwitcher.checked text: qsTr("TLS auth")
checked: tlsAuth
textAreaText: additionalClientCommands onCheckedChanged: {
placeholderText: qsTr("Commands:") if (checked !== tlsAuth) {
console.log("tlsAuth changed to: " + checked)
tlsAuth = checked
}
}
}
textArea.onEditingFinished: { DividerType {}
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText
}
}
}
SwitcherType { CheckBoxType {
id: additionalServerCommandsSwitcher id: blockDnsCheckBox
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
checked: additionalServerCommands !== "" text: qsTr("Block DNS requests outside of VPN")
checked: blockDns
text: qsTr("Additional server configuration commands") onCheckedChanged: {
if (checked !== blockDns) {
onCheckedChanged: { blockDns = checked
if (!checked) { }
additionalServerCommands = "" }
} }
} }
}
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: additionalServerCommandsSwitcher.checked
textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText
}
}
}
BasicButtonType {
id: saveButton
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); SwitcherType {
InstallController.updateContainer(OpenVpnConfigModel.getConfig()) id: additionalClientCommandsSwitcher
} Layout.fillWidth: true
var noButtonFunction = function() { Layout.topMargin: 32
if (!GC.isMobile()) { parentFlickable: fl
saveButton.forceActiveFocus()
checked: additionalClientCommands !== ""
text: qsTr("Additional client configuration commands")
onCheckedChanged: {
if (!checked) {
additionalClientCommands = ""
}
}
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
visible: additionalClientCommandsSwitcher.checked
parentFlickable: fl
textAreaText: additionalClientCommands
placeholderText: qsTr("Commands:")
textArea.onEditingFinished: {
if (additionalClientCommands !== textAreaText) {
additionalClientCommands = textAreaText
}
}
}
SwitcherType {
id: additionalServerCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
checked: additionalServerCommands !== ""
text: qsTr("Additional server configuration commands")
onCheckedChanged: {
if (!checked) {
additionalServerCommands = ""
}
}
}
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()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
+122 -108
View File
@@ -19,154 +19,164 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: header
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning() }
}
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: header.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
contentHeight: content.height
header: ColumnLayout { Column {
width: listView.width id: content
BaseHeaderType { anchors.top: parent.top
Layout.fillWidth: true anchors.left: parent.left
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16 anchors.topMargin: 32
Layout.bottomMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") ListView {
} id: listView
} width: parent.width
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel
model: ProtocolsModel activeFocusOnTab: true
focus: true
delegate: ColumnLayout { delegate: Item {
width: listView.width implicitWidth: parent.width
implicitHeight: delegateContent.implicitHeight
LabelWithButtonType { property alias focusItem: button
id: button
Layout.fillWidth: true ColumnLayout {
Layout.leftMargin: 16 id: delegateContent
Layout.rightMargin: 16
text: qsTr("Show connection options") anchors.fill: parent
clickedFunction: function() { LabelWithButtonType {
configContentDrawer.openTriggered() id: button
}
MouseArea { Layout.fillWidth: true
anchors.fill: button
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {} text: qsTr("Show connection options")
DrawerType2 { clickedFunction: function() {
id: configContentDrawer configContentDrawer.openTriggered()
}
expandedHeight: root.height * 0.9 MouseArea {
anchors.fill: button
parent: root cursorShape: Qt.PointingHandCursor
anchors.fill: parent enabled: false
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)
} }
} }
model: 1 // fake model to force the ListView to be created without a model DividerType {}
delegate: ColumnLayout { DrawerType2 {
width: drawerListView.width id: configContentDrawer
TextArea { expandedHeight: root.height * 0.9
id: configText
Layout.fillWidth: true parent: root
Layout.topMargin: 16 anchors.fill: parent
Layout.leftMargin: 16
Layout.rightMargin: 16
padding: 0 expandedStateContent: Item {
height: 24 implicitHeight: configContentDrawer.expandedHeight
color: AmneziaStyle.color.paleGray BackButtonType {
selectionColor: AmneziaStyle.color.richBrown id: backButton1
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16 anchors.top: parent.top
font.weight: Font.Medium anchors.left: parent.left
font.family: "PT Root UI VF" anchors.right: parent.right
anchors.topMargin: 16
text: rawConfig backButtonFunction: function() {
configContentDrawer.closeTriggered()
}
}
wrapMode: Text.Wrap 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
background: Rectangle { ColumnLayout {
color: AmneziaStyle.color.transparent 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
}
}
}
} }
} }
} }
} }
} }
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: removeButton id: removeButton
@@ -188,7 +198,11 @@ PageType {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
var noButtonFunction = function() {} var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
@@ -16,157 +16,178 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: ShadowSocksConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Shadowsocks settings") clip: true
} interactive: false
TextFieldWithHeaderType { model: ShadowSocksConfigModel
id: portTextField
Layout.fillWidth: true delegate: Item {
Layout.topMargin: 40 id: delegateItem
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
headerText: qsTr("Port") implicitWidth: listview.width
textField.text: port implicitHeight: col.implicitHeight
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { ColumnLayout {
if (textField.text !== port) { id: col
port = textField.text
}
}
checkEmptyText: true anchors.top: parent.top
} anchors.left: parent.left
anchors.right: parent.right
DropDownType { anchors.leftMargin: 16
id: cipherDropDown anchors.rightMargin: 16
Layout.fillWidth: true spacing: 0
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings")
}
descriptionText: qsTr("Cipher") TextFieldWithHeaderType {
headerText: qsTr("Cipher") id: portTextField
drawerParent: root Layout.fillWidth: true
Layout.topMargin: 40
listView: ListViewWithRadioButtonType { enabled: delegateItem.isEnabled
id: cipherListView headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
rootWidth: root.width textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
model: ListModel { checkEmptyText: true
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() { DropDownType {
cipherDropDown.text = selectedText id: cipherDropDown
cipher = cipherDropDown.text Layout.fillWidth: true
cipherDropDown.closeTriggered() Layout.topMargin: 20
}
Component.onCompleted: { enabled: delegateItem.isEnabled
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) { descriptionText: qsTr("Cipher")
if (cipherListView.model.get(i).name === cipherDropDown.text) { headerText: qsTr("Cipher")
currentIndex = i
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 { BasicButtonType {
id: saveButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: portTextField.errorText === "" enabled: portTextField.errorText === ""
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
var headerText = qsTr("Save settings?") 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 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 yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return return
} }
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ShadowSocksConfigModel.getConfig()) InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
saveButton.forceActiveFocus() saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
@@ -16,124 +16,160 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { Item {
id: backButton id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning() KeyNavigation.tab: listview.currentItem.mtuTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView {
id: listview
width: parent.width
height: listview.contentItem.height
clip: true
interactive: false
model: WireGuardConfigModel
delegate: Item {
id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
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
}
}
}
} }
} }
} }
ListViewType { BasicButtonType {
id: listView id: saveButton
anchors.top: backButton.bottom anchors.right: root.right
anchors.bottom: parent.bottom anchors.left: root.left
anchors.right: parent.right anchors.bottom: root.bottom
anchors.left: parent.left
model: WireGuardConfigModel anchors.topMargin: 24
anchors.bottomMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
delegate: ColumnLayout { enabled: listview.currentItem.isSaveButtonEnabled
width: listView.width
property alias mtuTextField: mtuTextField text: qsTr("Save")
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
spacing: 0 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")
BaseHeaderType { var yesButtonFunction = function() {
Layout.fillWidth: true if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
Layout.leftMargin: 16 PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
Layout.rightMargin: 16 return
headerText: qsTr("WG settings")
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("MTU")
textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== clientMtu) {
clientMtu = textField.text
}
} }
checkEmptyText: true
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
} }
var noButtonFunction = function() {
Header2TextType { if (!GC.isMobile()) {
Layout.fillWidth: true saveButton.forceActiveFocus()
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
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save")
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
}
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,130 +16,153 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: WireGuardConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() ListView {
id: listview
spacing: 0 width: parent.width
height: listview.contentItem.height
BaseHeaderType { clip: true
Layout.fillWidth: true interactive: false
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("WG settings") model: WireGuardConfigModel
}
TextFieldWithHeaderType { delegate: Item {
id: vpnAddressSubnetTextField id: delegateItem
Layout.fillWidth: true property alias focusItemId: vpnAddressSubnetTextField
Layout.topMargin: 40 property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("VPN address subnet") implicitWidth: listview.width
textField.text: subnetAddress implicitHeight: col.implicitHeight
textField.onEditingFinished: { ColumnLayout {
if (textField.text !== subnetAddress) { id: col
subnetAddress = textField.text
}
}
checkEmptyText: true anchors.top: parent.top
} anchors.left: parent.left
anchors.right: parent.right
TextFieldWithHeaderType { anchors.leftMargin: 16
id: portTextField anchors.rightMargin: 16
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Port") spacing: 0
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { BaseHeaderType {
if (textField.text !== port) { Layout.fillWidth: true
port = textField.text headerText: qsTr("WG settings")
}
}
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); TextFieldWithHeaderType {
InstallController.updateContainer(WireGuardConfigModel.getConfig()) id: vpnAddressSubnetTextField
} Layout.fillWidth: true
var noButtonFunction = function() { Layout.topMargin: 40
if (!GC.isMobile()) {
saveButton.forceActiveFocus() 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()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }
+132 -110
View File
@@ -17,141 +17,163 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.implicitHeight
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
model: XrayConfigModel id: content
delegate: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
property alias focusItemId: textFieldWithHeaderType.textField enabled: ServersModel.isProcessedServerHasWriteAccess()
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("XRay settings")
}
TextFieldWithHeaderType { clip: true
id: textFieldWithHeaderType interactive: false
Layout.fillWidth: true model: XrayConfigModel
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: listView.enabled delegate: Item {
id: delegateItem
headerText: qsTr("Disguised as traffic from") property alias focusItemId: textFieldWithHeaderType.textField
textField.text: site property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
textField.onEditingFinished: { implicitWidth: listview.width
if (textField.text !== site) { implicitHeight: col.implicitHeight
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
if (tmpText.startsWith("https://")) { ColumnLayout {
tmpText = textField.text.substring(8) id: col
site = tmpText
} else { anchors.top: parent.top
site = textField.text 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()
} }
} }
} }
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()
} }
} }
} }
} }
+30 -28
View File
@@ -16,53 +16,50 @@ import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 24
headerText: "AmneziaDNS" headerText: "AmneziaDNS"
descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") + 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.") 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 { LabelWithButtonType {
Layout.fillWidth: true id: removeButton
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.topMargin: 24
width: parent.width
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
@@ -74,14 +71,19 @@ PageType {
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& SettingsController.isAmneziaDnsEnabled()) { && SettingsController.isAmneziaDnsEnabled()) {
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server")) PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
} else { } else
{
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
} }
var noButtonFunction = function() {} var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
+227 -184
View File
@@ -24,215 +24,258 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
enabled: ServersModel.isProcessedServerHasWriteAccess() Column {
id: content
model: SftpConfigModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { enabled: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
spacing: 0 ListView {
id: listview
BaseHeaderType { width: parent.width
Layout.fillWidth: true height: listview.contentItem.height
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SFTP settings") clip: true
} interactive: false
LabelWithButtonType { model: SftpConfigModel
id: hostLabel
Layout.fillWidth: true onFocusChanged: {
Layout.topMargin: 32 if (focus) {
Layout.leftMargin: 16 listview.currentItem.listViewFocusItem.forceActiveFocus()
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
} }
MouseArea { delegate: Item {
anchors.fill: parent implicitWidth: listview.width
acceptedButtons: Qt.NoButton implicitHeight: col.implicitHeight
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
BasicButtonType { property alias listViewFocusItem: hostLabel.rightButton
id: detailedInstructionsButton
Layout.topMargin: 16 ColumnLayout {
Layout.bottomMargin: 16 id: col
Layout.leftMargin: 8
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent anchors.top: parent.top
hoveredColor: AmneziaStyle.color.translucentWhite anchors.left: parent.left
pressedColor: AmneziaStyle.color.sheerWhite anchors.right: parent.right
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Detailed instructions") spacing: 0
clickedFunc: function() { BaseHeaderType {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") 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")
}
}
}
} }
} }
} }
@@ -25,290 +25,327 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: listview.implicitHeight
anchors.left: parent.left
model: Socks5ProxyConfigModel ListView {
id: listview
delegate: ColumnLayout { width: parent.width
width: listView.width height: listview.contentItem.height
spacing: 0 clip: true
interactive: false
BaseHeaderType { model: Socks5ProxyConfigModel
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("SOCKS5 settings") onFocusChanged: {
} if (focus) {
listview.currentItem.focusItemId.forceActiveFocus()
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"))
} }
} }
LabelWithButtonType { delegate: Item {
Layout.fillWidth: true implicitWidth: listview.width
Layout.rightMargin: 16 implicitHeight: content.implicitHeight
Layout.bottomMargin: 16
text: qsTr("Port") property alias focusItemId: hostLabel.rightButton
descriptionText: port
descriptionOnTop: true ColumnLayout {
id: content
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.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
Connections { spacing: 0
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 { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: portTextField id: hostLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 32
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Port") parentFlickable: fl
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { text: qsTr("Host")
textField.text = textField.text.replace(/^\s+|\s+$/g, '') descriptionText: ServersModel.getProcessedServerData("hostName")
if (textField.text !== port) {
port = textField.text 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()
} }
} }
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: usernameTextField id: portLabel
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Username") text: qsTr("Port")
textField.placeholderText: "username" descriptionText: port
textField.text: username
textField.maximumLength: 32
textField.onEditingFinished: { descriptionOnTop: true
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== username) { parentFlickable: fl
username = textField.text
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()
} }
} }
} }
TextFieldWithHeaderType { LabelWithButtonType {
id: passwordTextField id: usernameLabel
property bool hidePassword: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("Password") text: qsTr("User name")
textField.placeholderText: "password" descriptionText: username
textField.text: password
textField.maximumLength: 32
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal descriptionOnTop: true
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
clickedFunc: function() { parentFlickable: fl
hidePassword = !hidePassword
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.onFocusChanged: { LabelWithButtonType {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') id: passwordLabel
if (textField.text !== password) { Layout.fillWidth: true
password = textField.text
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()
}
} }
} }
} }
BasicButtonType { BasicButtonType {
id: saveButton id: changeSettingsButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
clickedFunc: function() { clickedFunc: function() {
if (!portTextField.textField.acceptableInput) { forceActiveFocus()
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535") changeSettingsDrawer.openTriggered()
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,31 +25,34 @@ PageType {
} }
} }
BackButtonType { ColumnLayout {
id: backButton id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: { BackButtonType {
if (this.activeFocus) { id: backButton
listView.positionViewAtBeginning()
}
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -58,19 +61,11 @@ PageType {
headerText: qsTr("Tor website settings") 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 { LabelWithButtonType {
id: websiteName id: websiteName
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.bottomMargin: 24
text: qsTr("Website address") text: qsTr("Website address")
descriptionText: { descriptionText: {
@@ -88,16 +83,15 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
}
footer: ColumnLayout {
width: listView.width
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 40
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
+156 -163
View File
@@ -1,163 +1,156 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
ListViewType { FlickableType {
id: listView id: fl
anchors.top: parent.top
anchors.fill: parent anchors.bottom: parent.bottom
contentHeight: content.height
header: ColumnLayout {
width: listView.width ColumnLayout {
id: content
BaseHeaderType {
id: header anchors.top: parent.top
Layout.fillWidth: true anchors.left: parent.left
Layout.topMargin: 24 anchors.right: parent.right
Layout.bottomMargin: 16
Layout.rightMargin: 16 spacing: 0
Layout.leftMargin: 16
BaseHeaderType {
headerText: qsTr("Settings") id: header
} Layout.fillWidth: true
} Layout.topMargin: 24
Layout.rightMargin: 16
model: settingsEntries Layout.leftMargin: 16
delegate: ColumnLayout { headerText: qsTr("Settings")
width: listView.width }
spacing: 0 LabelWithButtonType {
id: account
LabelWithButtonType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 16
visible: isVisible text: qsTr("Servers")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
text: title leftImageSource: "qrc:/images/controls/server.svg"
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: leftImagePath clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsServersList)
clickedFunction: clickedHandler }
} }
DividerType { DividerType {}
visible: isVisible
} LabelWithButtonType {
} id: connection
Layout.fillWidth: true
footer: ColumnLayout {
width: listView.width text: qsTr("Connection")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
LabelWithButtonType { leftImageSource: "qrc:/images/controls/radio.svg"
id: close
clickedFunction: function() {
visible: GC.isDesktop() PageController.goToPage(PageEnum.PageSettingsConnection)
Layout.fillWidth: true }
}
text: qsTr("Close application")
leftImageSource: "qrc:/images/controls/x-circle.svg" DividerType {}
isLeftImageHoverEnabled: false
LabelWithButtonType {
clickedFunction: function() { id: application
PageController.closeApplication() Layout.fillWidth: true
}
} text: qsTr("Application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
DividerType { leftImageSource: "qrc:/images/controls/app.svg"
Layout.fillWidth: true
Layout.leftMargin: 16 clickedFunction: function() {
Layout.rightMargin: 16 PageController.goToPage(PageEnum.PageSettingsApplication)
}
visible: GC.isDesktop() }
}
} DividerType {}
}
LabelWithButtonType {
property list<QtObject> settingsEntries: [ id: backup
servers, Layout.fillWidth: true
connection,
application, text: qsTr("Backup")
backup, rightImageSource: "qrc:/images/controls/chevron-right.svg"
about, leftImageSource: "qrc:/images/controls/save.svg"
devConsole
] clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsBackup)
QtObject { }
id: servers }
property string title: qsTr("Servers") DividerType {}
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
property bool isVisible: true LabelWithButtonType {
readonly property var clickedHandler: function() { id: about
PageController.goToPage(PageEnum.PageSettingsServersList) Layout.fillWidth: true
}
} text: qsTr("About AmneziaVPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
QtObject { leftImageSource: "qrc:/images/controls/amnezia.svg"
id: connection
clickedFunction: function() {
property string title: qsTr("Connection") PageController.goToPage(PageEnum.PageSettingsAbout)
readonly property string leftImagePath: "qrc:/images/controls/radio.svg" }
property bool isVisible: true }
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsConnection) DividerType {}
}
} LabelWithButtonType {
id: devConsole
QtObject { visible: SettingsController.isDevModeEnabled
id: application Layout.fillWidth: true
property string title: qsTr("Application") text: qsTr("Dev console")
readonly property string leftImagePath: "qrc:/images/controls/app.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
property bool isVisible: true leftImageSource: "qrc:/images/controls/bug.svg"
readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsApplication) clickedFunction: function() {
} PageController.goToPage(PageEnum.PageDevMenu)
} }
}
QtObject {
id: backup DividerType {
visible: SettingsController.isDevModeEnabled
property string title: qsTr("Backup") }
readonly property string leftImagePath: "qrc:/images/controls/save.svg"
property bool isVisible: true LabelWithButtonType {
readonly property var clickedHandler: function() { id: close
PageController.goToPage(PageEnum.PageSettingsBackup) visible: GC.isDesktop()
} Layout.fillWidth: true
} Layout.preferredHeight: about.height
QtObject { text: qsTr("Close application")
id: about leftImageSource: "qrc:/images/controls/x-circle.svg"
isLeftImageHoverEnabled: false
property string title: qsTr("About AmneziaVPN")
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg" clickedFunction: function() {
property bool isVisible: true PageController.closeApplication()
readonly property var clickedHandler: function() { }
PageController.goToPage(PageEnum.PageSettingsAbout) }
}
} DividerType {
visible: GC.isDesktop()
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)
}
}
}
+85 -54
View File
@@ -29,7 +29,58 @@ PageType {
} }
} }
ListViewType { 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 {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@@ -37,6 +88,38 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left 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 { header: ColumnLayout {
width: listView.width width: listView.width
@@ -87,12 +170,11 @@ PageType {
} }
} }
model: contacts
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: telegramButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 6 Layout.topMargin: 6
@@ -175,55 +257,4 @@ 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,10 +119,6 @@ PageType {
checkable: !ConnectionController.isConnected checkable: !ConnectionController.isConnected
onClicked: { onClicked: {
if (ConnectionController.isConnectionInProgress) {
PageController.showNotificationMessage(qsTr("Unable change server location while trying to make an active connection"))
return
}
if (ConnectionController.isConnected) { if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change server location while there is an active connection"))
return return
@@ -20,49 +20,49 @@ PageType {
id: windows id: windows
readonly property string title: qsTr("Windows") readonly property string title: qsTr("Windows")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#windows") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#windows")
} }
QtObject { QtObject {
id: macos id: macos
readonly property string title: qsTr("macOS") readonly property string title: qsTr("macOS")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#macos") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#macos")
} }
QtObject { QtObject {
id: android id: android
readonly property string title: qsTr("Android") readonly property string title: qsTr("Android")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#android") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#android")
} }
QtObject { QtObject {
id: androidTv id: androidTv
readonly property string title: qsTr("AndroidTV") readonly property string title: qsTr("AndroidTV")
readonly property string link: qsTr("documentation/instructions/android_tv_connect/") readonly property string link: qsTr("https://docs.amnezia.org/ru/documentation/instructions/android_tv_connect/")
} }
QtObject { QtObject {
id: ios id: ios
readonly property string title: qsTr("iOS") readonly property string title: qsTr("iOS")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#ios") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#ios")
} }
QtObject { QtObject {
id: linux id: linux
readonly property string title: qsTr("Linux") readonly property string title: qsTr("Linux")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#linux") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#linux")
} }
QtObject { QtObject {
id: routers id: routers
readonly property string title: qsTr("Routers") readonly property string title: qsTr("Routers")
readonly property string link: qsTr("documentation/instructions/connect-amnezia-premium#routers") readonly property string link: qsTr("https://docs.amnezia.org/documentation/instructions/connect-amnezia-premium#routers")
} }
property list<QtObject> instructionsModel: [ property list<QtObject> instructionsModel: [
@@ -114,7 +114,7 @@ PageType {
rightImageSource: "qrc:/images/controls/external-link.svg" rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() { clickedFunction: function() {
Qt.openUrlExternally(LanguageModel.getCurrentDocsUrl(link)) Qt.openUrlExternally(link)
} }
} }
@@ -1,243 +1,220 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
property string configExtension: ".conf" property string configExtension: ".conf"
property string configCaption: qsTr("Save AmneziaVPN config") property string configCaption: qsTr("Save AmneziaVPN config")
BackButtonType { ListViewType {
id: backButton id: listView
anchors.top: parent.top anchors.fill: parent
anchors.left: parent.left anchors.topMargin: 20
anchors.right: parent.right anchors.bottomMargin: 24
anchors.topMargin: 20
model: ApiCountryModel
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) { header: ColumnLayout {
listView.positionViewAtBeginning() width: listView.width
}
} BackButtonType {
} id: backButton
}
ListViewType {
id: listView BaseHeaderType {
id: header
anchors.top: backButton.bottom
anchors.bottom: parent.bottom Layout.fillWidth: true
anchors.right: parent.right Layout.rightMargin: 16
anchors.left: parent.left Layout.leftMargin: 16
model: ApiCountryModel headerText: qsTr("Configuration Files")
descriptionText: qsTr("For router setup or the AmneziaWG app")
header: ColumnLayout { }
width: listView.width }
BaseHeaderType { delegate: ColumnLayout {
id: header width: listView.width
Layout.fillWidth: true LabelWithButtonType {
Layout.rightMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.topMargin: 6
headerText: qsTr("Configuration Files") text: countryName
descriptionText: qsTr("For router setup or the AmneziaWG app") descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
} descriptionColor: AmneziaStyle.color.vibrantRed
}
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
delegate: ColumnLayout { rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
width: listView.width
clickedFunction: function() {
LabelWithButtonType { if (isIssued) {
Layout.fillWidth: true moreOptionsDrawer.countryName = countryName
Layout.topMargin: 6 moreOptionsDrawer.countryCode = countryCode
moreOptionsDrawer.openTriggered()
text: countryName } else {
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : "" issueConfig(countryCode)
hideDescription: isWorkerExpired ? true : false }
descriptionColor: AmneziaStyle.color.vibrantRed }
}
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg" DividerType {}
}
clickedFunction: function() { }
if (isIssued) {
moreOptionsDrawer.countryName = countryName DrawerType2 {
moreOptionsDrawer.countryCode = countryCode id: moreOptionsDrawer
moreOptionsDrawer.openTriggered()
} else { property string countryName
issueConfig(countryCode) property string countryCode
}
} anchors.fill: parent
} expandedHeight: parent.height * 0.4375
DividerType {} expandedStateContent: Item {
} implicitHeight: moreOptionsDrawer.expandedHeight
}
BackButtonType {
DrawerType2 { id: moreOptionsDrawerBackButton
id: moreOptionsDrawer
anchors.top: parent.top
property string countryName anchors.left: parent.left
property string countryCode anchors.right: parent.right
anchors.topMargin: 16
anchors.fill: parent
expandedHeight: parent.height * 0.4375 backButtonFunction: function() {
moreOptionsDrawer.closeTriggered()
expandedStateContent: Item { }
implicitHeight: moreOptionsDrawer.expandedHeight }
BackButtonType { FlickableType {
id: moreOptionsDrawerBackButton anchors.top: moreOptionsDrawerBackButton.bottom
anchors.left: parent.left
anchors.top: parent.top anchors.right: parent.right
anchors.left: parent.left anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.topMargin: 16 contentHeight: moreOptionsDrawerContent.height
backButtonFunction: function() { ColumnLayout {
moreOptionsDrawer.closeTriggered() id: moreOptionsDrawerContent
}
} anchors.top: parent.top
anchors.left: parent.left
ListViewType { anchors.right: parent.right
id: drawerListView
Header2Type {
anchors.top: moreOptionsDrawerBackButton.bottom Layout.fillWidth: true
anchors.bottom: parent.bottom Layout.margins: 16
anchors.left: parent.left
anchors.right: parent.right headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
}
header: ColumnLayout {
width: drawerListView.width LabelWithButtonType {
Layout.fillWidth: true
Header2Type {
Layout.fillWidth: true text: qsTr("Generate a new configuration file")
Layout.margins: 16 descriptionText: qsTr("The previously created one will stop working")
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") clickedFunction: function() {
} showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
} }
}
model: 1 // fake model to force the ListView to be created without a model
DividerType {}
delegate: ColumnLayout {
width: drawerListView.width LabelWithButtonType {
Layout.fillWidth: true
LabelWithButtonType { text: qsTr("Revoke the current configuration file")
Layout.fillWidth: true
Layout.leftMargin: 16 clickedFunction: function() {
Layout.rightMargin: 16 showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
}
text: qsTr("Generate a new configuration file") }
descriptionText: qsTr("The previously created one will stop working")
DividerType {}
clickedFunction: function() { }
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) }
} }
} }
DividerType {} function issueConfig(countryCode) {
} var fileName = ""
if (GC.isMobile()) {
footer: ColumnLayout { fileName = countryCode + configExtension
width: drawerListView.width } else {
fileName = SystemController.getFileName(configCaption,
LabelWithButtonType { qsTr("Config files (*" + configExtension + ")"),
Layout.fillWidth: true StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
Layout.leftMargin: 16 true,
Layout.rightMargin: 16 configExtension)
}
text: qsTr("Revoke the current configuration file") if (fileName !== "") {
PageController.showBusyIndicator(true)
clickedFunction: function() { let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) if (result) {
} ApiSettingsController.getAccountInfo(true)
} }
DividerType {} PageController.showBusyIndicator(false)
} if (result) {
} PageController.showNotificationMessage(qsTr("Config file saved"))
} }
} }
}
function issueConfig(countryCode) {
var fileName = "" function revokeConfig(countryCode) {
if (GC.isMobile()) { PageController.showBusyIndicator(true)
fileName = countryCode + configExtension let result = ApiConfigsController.revokeNativeConfig(countryCode)
} else { if (result) {
fileName = SystemController.getFileName(configCaption, ApiSettingsController.getAccountInfo(true)
qsTr("Config files (*" + configExtension + ")"), }
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode, PageController.showBusyIndicator(false)
true,
configExtension) if (result) {
} PageController.showNotificationMessage(qsTr("The config has been revoked"))
if (fileName !== "") { }
PageController.showBusyIndicator(true) }
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
if (result) { function showQuestion(isConfigIssue, countryCode, countryName) {
ApiSettingsController.getAccountInfo(true) var headerText
} if (isConfigIssue) {
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
PageController.showBusyIndicator(false) } else {
if (result) { headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
PageController.showNotificationMessage(qsTr("Config file saved")) }
}
} 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")
function revokeConfig(countryCode) {
PageController.showBusyIndicator(true) var yesButtonFunction = function() {
let result = ApiConfigsController.revokeNativeConfig(countryCode) if (isConfigIssue) {
if (result) { issueConfig(countryCode)
ApiSettingsController.getAccountInfo(true) } else {
} revokeConfig(countryCode)
PageController.showBusyIndicator(false) }
moreOptionsDrawer.closeTriggered()
if (result) { }
PageController.showNotificationMessage(qsTr("The config has been revoked")) var noButtonFunction = function() {
} }
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
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,6 +111,17 @@ PageType {
serverNameEditDrawer.openTriggered() serverNameEditDrawer.openTriggered()
} }
} }
RenameServerDrawer {
id: serverNameEditDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.35
serverNameText: root.processedServer.name
}
} }
delegate: ColumnLayout { delegate: ColumnLayout {
@@ -187,13 +198,7 @@ PageType {
iconPath: "qrc:/images/controls/alert-circle.svg" iconPath: "qrc:/images/controls/alert-circle.svg"
visible: { visible: ApiAccountInfoModel.data("hasExpiredWorker")
for (let i = 0; i < ApiCountryModel.count; ++i) {
if (ApiCountryModel.get(i).isWorkerExpired)
return true;
}
return false;
}
} }
LabelWithButtonType { LabelWithButtonType {
@@ -202,30 +207,35 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32 Layout.topMargin: warning.visible ? 16 : 32
visible: footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
text: qsTr("Subscription Key") text: qsTr("Subscription Key")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsApiSubscriptionKey) 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.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiConfigsController.prepareVpnKeyExport() ApiConfigsController.prepareVpnKeyExport()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
// Navigate to PageShareConnection page
//PageController.goToPage(PageEnum.PageShareConnection)
} }
} }
DividerType { DividerType {
visible: footer.isVisibleForAmneziaFree visible: false //footer.isVisibleForAmneziaFree
} }
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree
@@ -410,12 +420,9 @@ PageType {
} }
} }
RenameServerDrawer { ShareConnectionDrawer {
id: serverNameEditDrawer id: shareConnectionDrawer
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.35
serverNameText: root.processedServer.name
} }
} }
@@ -1,204 +0,0 @@
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,7 +50,6 @@ PageType {
readonly property string name: qsTr("Only the apps from the list should have access via VPN") readonly property string name: qsTr("Only the apps from the list should have access via VPN")
readonly property int type: routeMode.onlyForwardApps readonly property int type: routeMode.onlyForwardApps
} }
QtObject { QtObject {
id: allExceptApps id: allExceptApps
@@ -112,7 +111,7 @@ PageType {
headerText: qsTr("Mode") headerText: qsTr("Mode")
enabled: (Qt.platform.os === "android") && root.pageEnabled enabled: Qt.platform.os === "android" && root.pageEnabled
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
rootWidth: root.width rootWidth: root.width
@@ -147,56 +146,77 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.bottom: addAppButton.top anchors.topMargin: 16
anchors.left: parent.left contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin
anchors.right: parent.right
model: SortFilterProxyModel { enabled: root.pageEnabled
id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter {
roleName: "appPath"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
delegate: ColumnLayout { Column {
width: listView.width id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType { ListView {
Layout.fillWidth: true id: apps
width: parent.width
height: apps.contentItem.height
Layout.leftMargin: 16 model: SortFilterProxyModel {
Layout.rightMargin: 16 id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
text: appPath filters: RegExpFilter {
rightImageSource: "qrc:/images/controls/trash.svg" roleName: "appPath"
rightImageColor: AmneziaStyle.color.paleGray pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
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() {
} }
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) 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 {}
}
} }
} }
DividerType {}
} }
} }
@@ -21,24 +21,22 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -47,17 +45,9 @@ PageType {
headerText: qsTr("Application") 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 { SwitcherType {
id: switcherAllowScreenshots id: switcher
visible: GC.isMobile() visible: GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -71,6 +61,10 @@ PageType {
SettingsController.toggleScreenshotsEnabled(checked) SettingsController.toggleScreenshotsEnabled(checked)
} }
} }
// KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
// labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
parentFlickable: fl
} }
DividerType { DividerType {
@@ -79,15 +73,15 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonNotification id: labelWithButtonNotification
visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Enable notifications") text: qsTr("Enable notifications")
descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") descriptionText: qsTr("Enable notifications to show the VPN state in the status bar")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
SettingsController.requestNotificationPermission() SettingsController.requestNotificationPermission()
} }
@@ -99,7 +93,6 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoStart id: switcherAutoStart
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -108,6 +101,8 @@ PageType {
text: qsTr("Auto start") text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts") descriptionText: qsTr("Launch the application every time the device is starts")
parentFlickable: fl
checked: SettingsController.isAutoStartEnabled() checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoStartEnabled()) { if (checked !== SettingsController.isAutoStartEnabled()) {
@@ -122,7 +117,6 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoConnect id: switcherAutoConnect
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@@ -131,6 +125,8 @@ PageType {
text: qsTr("Auto connect") text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start") descriptionText: qsTr("Connect to VPN on app start")
parentFlickable: fl
checked: SettingsController.isAutoConnectEnabled() checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) { if (checked !== SettingsController.isAutoConnectEnabled()) {
@@ -145,17 +141,15 @@ PageType {
SwitcherType { SwitcherType {
id: switcherStartMinimized id: switcherStartMinimized
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
text: qsTr("Start minimized") text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized (works with autostart option turned on)") descriptionText: qsTr("Launch application minimized")
enabled: switcherAutoStart.checked parentFlickable: fl
opacity: enabled ? 1.0 : 0.5
checked: SettingsController.isStartMinimizedEnabled() checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: { onCheckedChanged: {
@@ -168,21 +162,17 @@ PageType {
DividerType { DividerType {
visible: !GC.isMobile() visible: !GC.isMobile()
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLanguage id: labelWithButtonLanguage
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Language") text: qsTr("Language")
descriptionText: LanguageModel.currentLanguageName descriptionText: LanguageModel.currentLanguageName
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
selectLanguageDrawer.openTriggered() selectLanguageDrawer.openTriggered()
} }
@@ -192,13 +182,14 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLogging id: labelWithButtonLogging
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Logging") text: qsTr("Logging")
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
} }
@@ -208,13 +199,14 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonReset id: labelWithButtonReset
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Reset settings and remove all data from the application") text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Reset settings and remove all data from the application?") 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.") var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
+16 -34
View File
@@ -41,68 +41,51 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
id: content
width: listView.width anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Back up your configuration") 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.") 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 { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
textString: qsTr("The backup will contain your passwords and private keys for all servers added " + 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" iconPath: "qrc:/images/controls/alert-circle.svg"
} }
BasicButtonType { BasicButtonType {
id: makeBackupButton id: makeBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 14 Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Make a backup") text: qsTr("Make a backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@@ -125,11 +108,8 @@ PageType {
BasicButtonType { BasicButtonType {
id: restoreBackupButton id: restoreBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@@ -140,6 +120,8 @@ PageType {
text: qsTr("Restore from backup") text: qsTr("Restore from backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var filePath = SystemController.getFileName(qsTr("Open backup file"), var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)")) qsTr("Backup files (*.backup)"))
+16 -30
View File
@@ -21,25 +21,20 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height
anchors.right: parent.right
header: ColumnLayout { ColumnLayout {
id: content
width: listView.width anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -48,17 +43,9 @@ PageType {
headerText: qsTr("Connection") 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 { SwitcherType {
id: amneziaDnsSwitch id: amneziaDnsSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
@@ -77,13 +64,14 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: dnsServersButton id: dnsServersButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("DNS servers") text: qsTr("DNS servers")
descriptionText: qsTr("When AmneziaDNS is not used or installed") descriptionText: qsTr("When AmneziaDNS is not used or installed")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsDns) PageController.goToPage(PageEnum.PageSettingsDns)
} }
@@ -93,13 +81,14 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton id: splitTunnelingButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: qsTr("Allows you to select which sites you want to access through the VPN") descriptionText: qsTr("Allows you to select which sites you want to access through the VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
} }
@@ -107,15 +96,8 @@ PageType {
DividerType {} DividerType {}
}
footer: ColumnLayout { // TODO(CyAn84): move to delegate,add DelegateChooser when have migrated to 6.9
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
@@ -124,6 +106,8 @@ PageType {
descriptionText: qsTr("Allows you to use the VPN only for certain Apps") descriptionText: qsTr("Allows you to use the VPN only for certain Apps")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
} }
@@ -143,6 +127,8 @@ PageType {
descriptionText: qsTr("Blocks network connections without VPN") descriptionText: qsTr("Blocks network connections without VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsKillSwitch) PageController.goToPage(PageEnum.PageSettingsKillSwitch)
} }
+140 -163
View File
@@ -1,163 +1,140 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Config" import "../Config"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { BackButtonType {
id: backButton id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
onFocusChanged: {
if (this.activeFocus) { FlickableType {
listView.positionViewAtBeginning() id: fl
} anchors.top: backButton.bottom
} anchors.bottom: parent.bottom
} contentHeight: content.height
ListViewType { property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
id: listView
enabled: !isServerFromApi
anchors.top: backButton.bottom
anchors.bottom: parent.bottom Component.onCompleted: {
anchors.right: parent.right if (isServerFromApi) {
anchors.left: parent.left PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
}
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex) }
enabled: !isServerFromApi ColumnLayout {
id: content
Component.onCompleted: {
if (isServerFromApi) { anchors.top: parent.top
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS")) anchors.left: parent.left
} anchors.right: parent.right
} anchors.leftMargin: 16
anchors.rightMargin: 16
header: ColumnLayout {
width: listView.width spacing: 16
spacing: 16
BaseHeaderType {
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true
Layout.leftMargin: 16 headerText: qsTr("DNS servers")
Layout.rightMargin: 16 }
headerText: qsTr("DNS servers") ParagraphTextType {
} Layout.fillWidth: true
text: qsTr("If AmneziaDNS is not used or installed")
ParagraphTextType { }
Layout.fillWidth: true
Layout.leftMargin: 16 TextFieldWithHeaderType {
Layout.rightMargin: 16 id: primaryDns
text: qsTr("If AmneziaDNS is not used or installed") Layout.fillWidth: true
} headerText: qsTr("Primary DNS")
}
textField.text: SettingsController.primaryDns
model: 1 // fake model to force the ListView to be created without a model textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp()
delegate: ColumnLayout { }
width: listView.width }
spacing: 16
TextFieldWithHeaderType {
TextFieldWithHeaderType { id: secondaryDns
id: primaryDns
Layout.fillWidth: true
Layout.fillWidth: true headerText: qsTr("Secondary DNS")
Layout.leftMargin: 16
Layout.rightMargin: 16 textField.text: SettingsController.secondaryDns
textField.validator: RegularExpressionValidator {
headerText: qsTr("Primary DNS") regularExpression: InstallController.ipAddressRegExp()
}
textField.text: SettingsController.primaryDns }
textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp() BasicButtonType {
} id: restoreDefaultButton
} Layout.fillWidth: true
TextFieldWithHeaderType { defaultColor: AmneziaStyle.color.transparent
id: secondaryDns hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
Layout.fillWidth: true disabledColor: AmneziaStyle.color.mutedGray
Layout.leftMargin: 16 textColor: AmneziaStyle.color.paleGray
Layout.rightMargin: 16 borderWidth: 1
headerText: qsTr("Secondary DNS") text: qsTr("Restore default")
textField.text: SettingsController.secondaryDns clickedFunc: function() {
textField.validator: RegularExpressionValidator { var headerText = qsTr("Restore default DNS settings?")
regularExpression: InstallController.ipAddressRegExp() var yesButtonText = qsTr("Continue")
} var noButtonText = qsTr("Cancel")
}
var yesButtonFunction = function() {
BasicButtonType { SettingsController.primaryDns = "1.1.1.1"
id: restoreDefaultButton primaryDns.textField.text = SettingsController.primaryDns
SettingsController.secondaryDns = "1.0.0.1"
Layout.fillWidth: true secondaryDns.textField.text = SettingsController.secondaryDns
Layout.topMargin: 16 PageController.showNotificationMessage(qsTr("Settings have been reset"))
Layout.leftMargin: 16 }
Layout.rightMargin: 16 var noButtonFunction = function() {
}
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
pressedColor: AmneziaStyle.color.sheerWhite }
disabledColor: AmneziaStyle.color.mutedGray }
textColor: AmneziaStyle.color.paleGray
borderWidth: 1 BasicButtonType {
id: saveButton
text: qsTr("Restore default")
Layout.fillWidth: true
clickedFunc: function() {
var headerText = qsTr("Restore default DNS settings?") text: qsTr("Save")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") clickedFunc: function() {
if (primaryDns.textField.text !== SettingsController.primaryDns) {
var yesButtonFunction = function() { SettingsController.primaryDns = primaryDns.textField.text
SettingsController.primaryDns = "1.1.1.1" }
primaryDns.textField.text = SettingsController.primaryDns if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
SettingsController.secondaryDns = "1.0.0.1" SettingsController.secondaryDns = secondaryDns.textField.text
secondaryDns.textField.text = SettingsController.secondaryDns }
PageController.showNotificationMessage(qsTr("Settings have been reset")) PageController.showNotificationMessage(qsTr("Settings saved"))
} }
var noButtonFunction = function() { }
} }
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
}
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,9 +71,6 @@ PageType {
onClicked: function() { onClicked: function() {
SettingsController.strictKillSwitchEnabled = false SettingsController.strictKillSwitchEnabled = false
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
DividerType {} DividerType {}
@@ -106,9 +103,6 @@ PageType {
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
DividerType { DividerType {
@@ -64,7 +64,7 @@ PageType {
displayMarginBeginning: 40 displayMarginBeginning: 40
displayMarginEnd: 40 displayMarginEnd: 40
ScrollBar.vertical: ScrollBarType {} ScrollBar.vertical: ScrollBarType { }
footer: Item { footer: Item {
width: listView.width width: listView.width
+7 -8
View File
@@ -23,15 +23,9 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@@ -39,6 +33,10 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@@ -103,7 +101,8 @@ PageType {
} }
model: logTypes model: logTypes
clip: true
reuseItems: true
snapMode: ListView.SnapOneItem snapMode: ListView.SnapOneItem
delegate: ColumnLayout { delegate: ColumnLayout {
+163 -135
View File
@@ -18,7 +18,9 @@ PageType {
signal lastItemTabClickedSignal() signal lastItemTabClickedSignal()
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess() onFocusChanged: content.isServerWithWriteAccess ?
labelWithButton.forceActiveFocus() :
labelWithButton3.forceActiveFocus()
Connections { Connections {
target: InstallController target: InstallController
@@ -61,192 +63,218 @@ PageType {
target: ServersModel target: ServersModel
function onProcessedServerIndexChanged() { function onProcessedServerIndexChanged() {
root.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess() content.isServerWithWriteAccess = ServersModel.isProcessedServerHasWriteAccess()
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
anchors.fill: parent ColumnLayout {
id: content
model: serverActions anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButton
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
visible: isVisible text: qsTr("Check the server for previously installed Amnezia services")
descriptionText: qsTr("Add them to the application if they were not displayed")
text: title
descriptionText: description
textColor: tColor
clickedFunction: function() { clickedFunction: function() {
clickedHandler() PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
} }
} }
DividerType { DividerType {
visible: isVisible visible: content.isServerWithWriteAccess
} }
}
}
property list<QtObject> serverActions: [ LabelWithButtonType {
check, id: labelWithButton2
reboot, visible: content.isServerWithWriteAccess
remove, Layout.fillWidth: true
clear,
reset,
switch_to_premium,
]
QtObject { text: qsTr("Reboot server")
id: check textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: root.isServerWithWriteAccess clickedFunction: function() {
readonly property string title: qsTr("Check the server for previously installed Amnezia services") var headerText = qsTr("Do you want to reboot the server?")
readonly property string description: qsTr("Add them to the application if they were not displayed") var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
readonly property var tColor: AmneziaStyle.color.paleGray var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
}
}
QtObject { var yesButtonFunction = function() {
id: reboot 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()
}
}
property bool isVisible: root.isServerWithWriteAccess showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
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.rebootProcessedServer()
PageController.showBusyIndicator(false)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton3
} Layout.fillWidth: true
QtObject { text: qsTr("Remove server from application")
id: remove textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: true clickedFunction: function() {
readonly property string title: qsTr("Remove server from application") var headerText = qsTr("Do you want to remove the server from application?")
readonly property string description: "" var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
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() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection")) PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.removeProcessedServer() InstallController.removeProcessedServer()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton3.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
} DividerType {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton4
} visible: content.isServerWithWriteAccess
Layout.fillWidth: true
QtObject { text: qsTr("Clear server from Amnezia software")
id: clear textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: root.isServerWithWriteAccess clickedFunction: function() {
readonly property string title: qsTr("Clear server from Amnezia software") var headerText = qsTr("Do you want to clear server from Amnezia software?")
readonly property string description: "" var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
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() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection")) PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else { } else {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers() InstallController.removeAllContainers()
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton4.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: content.isServerWithWriteAccess
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton5
} visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
Layout.fillWidth: true
QtObject { text: qsTr("Reset API config")
id: reset textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") clickedFunction: function() {
readonly property string title: qsTr("Reset API config") var headerText = qsTr("Do you want to reset API config?")
readonly property string description: "" var descriptionText = ""
readonly property var tColor: AmneziaStyle.color.vibrantRed var yesButtonText = qsTr("Continue")
readonly property var clickedHandler: function() { var noButtonText = qsTr("Cancel")
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) { if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection")) PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.removeApiConfig(ServersModel.processedIndex) InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var noButtonFunction = function() {
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: labelWithButton6
} visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
Layout.fillWidth: true
QtObject { text: qsTr("Switch to the new Amnezia Premium subscription")
id: switch_to_premium textColor: AmneziaStyle.color.vibrantRed
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium clickedFunction: function() {
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription") PageController.goToPageHome()
readonly property string description: "" ApiPremV1MigrationController.showMigrationDrawer()
readonly property var tColor: AmneziaStyle.color.vibrantRed }
readonly property var clickedHandler: function() { }
PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer() DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi") && ServersModel.processedServerIsPremium
}
} }
} }
} }
@@ -21,211 +21,249 @@ PageType {
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
BackButtonType { ColumnLayout {
id: backButton id: header
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onFocusChanged: {
if (this.activeFocus) {
listView.positionViewAtBeginning()
}
}
}
ListViewType { BackButtonType {
id: listView id: backButton
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.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 32
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
}
} }
model: ProtocolsModel BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 32
delegate: ColumnLayout { headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
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 { ListView {
id: protocols
Layout.fillWidth: true
height: protocols.contentItem.height
clip: true
interactive: true
width: listView.width property bool isFocusable: true
LabelWithButtonType { Keys.onTabPressed: {
id: clearCacheButton FocusController.nextKeyTabItem()
}
Layout.fillWidth: true Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
visible: root.isClearCacheVisible Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
text: qsTr("Clear profile") Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
clickedFunction: function() { Keys.onLeftPressed: {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName()) FocusController.nextKeyLeftItem()
var descriptionText = qsTr("The connection configuration will be deleted for this device only") }
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { Keys.onRightPressed: {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { FocusController.nextKeyRightItem()
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName()) }
PageController.showNotificationMessage(message)
return 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"))
}
} }
PageController.showBusyIndicator(true) MouseArea {
InstallController.clearCachedProfile() anchors.fill: clientSettings
PageController.showBusyIndicator(false) cursorShape: Qt.PointingHandCursor
} enabled: 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() {
DividerType {
visible: delegateContent.isClientSettingsVisible
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) LabelWithButtonType {
} id: serverSettings
MouseArea { Layout.fillWidth: true
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor text: protocolName + qsTr(" server settings")
enabled: false 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
}
} }
} }
DividerType { footer: ColumnLayout {
visible: ServersModel.isProcessedServerHasWriteAccess() 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()
}
} }
} }
} }
} }
@@ -40,20 +40,25 @@ PageType {
} }
} }
ListViewType { ListView {
id: servers id: servers
objectName: "servers" objectName: "servers"
width: parent.width width: parent.width
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: 500
property bool isFocusable: true
model: ServersModel model: ServersModel
clip: true
reuseItems: true
delegate: Item { delegate: Item {
implicitWidth: servers.width implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
@@ -161,7 +161,7 @@ PageType {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
@@ -172,6 +172,8 @@ PageType {
enabled: root.pageEnabled enabled: root.pageEnabled
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxySitesModel id: proxySitesModel
sourceModel: SitesModel sourceModel: SitesModel
@@ -191,7 +193,13 @@ PageType {
] ]
} }
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
id: delegateContent
width: listView.width width: listView.width
LabelWithButtonType { LabelWithButtonType {
@@ -228,6 +236,7 @@ PageType {
} }
} }
Rectangle { Rectangle {
anchors.fill: addSiteButton anchors.fill: addSiteButton
anchors.bottomMargin: -24 anchors.bottomMargin: -24
@@ -299,7 +308,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
headerText: qsTr("Additional options") headerText: qsTr("Import / Export Sites")
} }
LabelWithButtonType { LabelWithButtonType {
@@ -342,34 +351,6 @@ PageType {
} }
DividerType {} 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 {}
} }
} }
@@ -393,24 +374,22 @@ PageType {
backButtonFunction: function() { backButtonFunction: function() {
importSitesDrawer.closeTriggered() importSitesDrawer.closeTriggered()
} }
onFocusChanged: {
if (this.activeFocus) {
importSitesDrawerListView.positionViewAtBeginning()
}
}
} }
ListViewType { FlickableType {
id: importSitesDrawerListView
anchors.top: importSitesDrawerBackButton.bottom anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
header: ColumnLayout { contentHeight: importSitesDrawerContent.height
width: importSitesDrawerListView.width
ColumnLayout {
id: importSitesDrawerContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
@@ -418,67 +397,49 @@ PageType {
headerText: qsTr("Import a list of sites") headerText: qsTr("Import a list of sites")
} }
}
model: importOptions
delegate: ColumnLayout {
width: importSitesDrawerListView.width
LabelWithButtonType { LabelWithButtonType {
id: importSitesButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: title text: qsTr("Replace site list")
clickedFunction: function() { clickedFunction: function() {
clickedHandler() var fileName = SystemController.getFileName(qsTr("Open sites file"),
qsTr("Sites files (*.json)"))
if (fileName !== "") {
importSitesDrawerContent.importSites(fileName, true)
}
} }
} }
DividerType {} 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,177 +1,146 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { FlickableType {
id: backButton id: fl
anchors.top: parent.top
anchors.top: parent.top anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin
anchors.right: parent.right
anchors.topMargin: 20 ColumnLayout {
id: content
onFocusChanged: {
if (this.activeFocus) { anchors.top: parent.top
listView.positionViewAtBeginning() anchors.left: parent.left
} anchors.right: parent.right
}
} spacing: 0
ListViewType { BackButtonType {
id: listView id: backButton
Layout.topMargin: 20
anchors.top: backButton.bottom }
anchors.bottom: parent.bottom
anchors.right: parent.right BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
Layout.topMargin: 8
header: ColumnLayout { Layout.rightMargin: 16
width: listView.width Layout.leftMargin: 16
Layout.bottomMargin: 32
BaseHeaderType {
Layout.fillWidth: true headerText: ApiServicesModel.getSelectedServiceData("name")
Layout.topMargin: 8 descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
Layout.rightMargin: 16 }
Layout.leftMargin: 16
Layout.bottomMargin: 32 LabelWithImageType {
Layout.fillWidth: true
headerText: ApiServicesModel.getSelectedServiceData("name") Layout.margins: 16
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
} imageSource: "qrc:/images/controls/map-pin.svg"
} leftText: qsTr("For the region")
rightText: ApiServicesModel.getSelectedServiceData("region")
model: inputFields }
spacing: 0
LabelWithImageType {
delegate: ColumnLayout { Layout.fillWidth: true
width: listView.width Layout.margins: 16
LabelWithImageType { imageSource: "qrc:/images/controls/tag.svg"
Layout.fillWidth: true leftText: qsTr("Price")
Layout.margins: 16 rightText: ApiServicesModel.getSelectedServiceData("price")
}
imageSource: imagePath
leftText: lText LabelWithImageType {
rightText: rText Layout.fillWidth: true
} Layout.margins: 16
}
imageSource: "qrc:/images/controls/history.svg"
footer: ColumnLayout { leftText: qsTr("Work period")
width: listView.width rightText: ApiServicesModel.getSelectedServiceData("timeLimit")
spacing: 0 visible: rightText !== ""
}
ParagraphTextType {
Layout.fillWidth: true LabelWithImageType {
Layout.rightMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16 Layout.margins: 16
onLinkActivated: function(link) { imageSource: "qrc:/images/controls/gauge.svg"
Qt.openUrlExternally(link) leftText: qsTr("Speed")
} rightText: ApiServicesModel.getSelectedServiceData("speed")
textFormat: Text.RichText }
text: {
var text = ApiServicesModel.getSelectedServiceData("features") LabelWithImageType {
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")) Layout.fillWidth: true
} Layout.margins: 16
MouseArea { imageSource: "qrc:/images/controls/info.svg"
anchors.fill: parent leftText: qsTr("Features")
acceptedButtons: Qt.NoButton rightText: ""
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor }
}
} ParagraphTextType {
Layout.fillWidth: true
BasicButtonType { Layout.rightMargin: 16
id: continueButton Layout.leftMargin: 16
Layout.fillWidth: true onLinkActivated: function(link) {
Layout.topMargin: 32 Qt.openUrlExternally(link)
Layout.bottomMargin: 32 }
Layout.leftMargin: 16 textFormat: Text.RichText
Layout.rightMargin: 16 text: {
var text = ApiServicesModel.getSelectedServiceData("features")
text: qsTr("Connect") return text.replace("%1", LanguageModel.getCurrentSiteUrl())
}
clickedFunc: function() {
var endpoint = ApiServicesModel.getStoreEndpoint() MouseArea {
if (endpoint !== undefined && endpoint !== "") { anchors.fill: parent
Qt.openUrlExternally(endpoint) acceptedButtons: Qt.NoButton
PageController.closePage() cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
PageController.closePage() }
} else { }
PageController.showBusyIndicator(true) }
ApiConfigsController.importServiceFromGateway() }
PageController.showBusyIndicator(false)
} BasicButtonType {
} id: continueButton
}
} anchors.right: parent.right
} anchors.left: parent.left
anchors.bottom: parent.bottom
property list<QtObject> inputFields: [
region, anchors.topMargin: 32
price, anchors.rightMargin: 16
timeLimit, anchors.leftMargin: 16
speed, anchors.bottomMargin: 32
features
] text: qsTr("Connect")
QtObject { clickedFunc: function() {
id: region var endpoint = ApiServicesModel.getStoreEndpoint()
if (endpoint !== undefined && endpoint !== "") {
readonly property string imagePath: "qrc:/images/controls/map-pin.svg" Qt.openUrlExternally(endpoint)
readonly property string lText: qsTr("For the region") PageController.closePage()
readonly property string rText: ApiServicesModel.getSelectedServiceData("region") PageController.closePage()
property bool isVisible: true } else {
} PageController.showBusyIndicator(true)
ApiConfigsController.importServiceFromGateway()
QtObject { PageController.showBusyIndicator(false)
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,77 +14,86 @@ import "../Config"
PageType { PageType {
id: root id: root
BackButtonType { ColumnLayout {
id: backButton id: header
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
Layout.topMargin: 20
onActiveFocusChanged: { spacing: 0
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning() 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.")
} }
} }
ListViewType { ListView {
id: listView id: servicesListView
anchors.top: backButton.bottom anchors.top: header.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: 16 anchors.topMargin: 16
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 spacing: 0
property bool isFocusable: true
clip: true
reuseItems: true
model: ApiServicesModel model: ApiServicesModel
delegate: ColumnLayout { ScrollBar.vertical: ScrollBarType {}
width: listView.width delegate: Item {
implicitWidth: servicesListView.width
implicitHeight: delegateContent.implicitHeight
enabled: isServiceAvailable enabled: isServiceAvailable
CardWithIconsType { ColumnLayout {
id: card id: delegateContent
Layout.fillWidth: true anchors.fill: parent
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: name CardWithIconsType {
bodyText: cardDescription id: card
footerText: price
rightImageSource: "qrc:/images/controls/chevron-right.svg" Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
onClicked: { headerText: name
if (isServiceAvailable) { bodyText: cardDescription
ApiServicesModel.setServiceIndex(index) footerText: price
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: {
if (isServiceAvailable) {
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
} }
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
} }
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
} }
} }
} }
@@ -27,11 +27,21 @@ PageType {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.fill: parent anchors.fill: parent
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: variants
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@@ -206,8 +216,6 @@ PageType {
} }
} }
model: variants
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@@ -226,9 +234,6 @@ PageType {
leftImageSource: imageSource leftImageSource: imageSource
onClicked: { handler() } onClicked: { handler() }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
@@ -28,14 +28,41 @@ PageType {
} }
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left 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 { header: ColumnLayout {
width: listView.width width: listView.width
@@ -51,6 +78,8 @@ PageType {
model: inputFields model: inputFields
spacing: 16 spacing: 16
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@@ -148,9 +177,6 @@ PageType {
onClicked: { onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide")) Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl("starter-guide"))
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
} }
+79 -87
View File
@@ -20,7 +20,6 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@@ -41,98 +40,104 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ButtonGroup { FlickableType {
id: buttonGroup id: fl
}
ListViewType {
id: listView
property int dockerContainer
property int containerDefaultPort
property int containerDefaultTransportProto
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin
anchors.right: parent.right
spacing: 16 Column {
id: content
header: ColumnLayout { anchors.top: parent.top
width: listView.width anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
id: header id: header
Layout.fillWidth: true implicitWidth: parent.width
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerTextMaximumLineCount: 10 headerTextMaximumLineCount: 10
headerText: qsTr("Choose Installation Type") headerText: qsTr("Choose Installation Type")
} }
}
model: proxyContainersModel ButtonGroup {
currentIndex: 0 id: buttonGroup
}
delegate: ColumnLayout { ListView {
width: listView.width id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
CardType { currentIndex: 0
id: card clip: true
interactive: false
model: proxyContainersModel
Layout.fillWidth: true property int dockerContainer
Layout.rightMargin: 16 property int containerDefaultPort
Layout.leftMargin: 16 property int containerDefaultTransportProto
Layout.bottomMargin: 16
headerText: easySetupHeader property bool isFocusable: true
bodyText: easySetupDescription
ButtonGroup.group: buttonGroup delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
onClicked: function() { ColumnLayout {
isEasySetup = true id: delegateContent
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
listView.dockerContainer = dockerContainer anchors.top: parent.top
listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto) anchors.left: parent.left
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto) 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)
}
}
}
} }
Keys.onReturnPressed: this.clicked() Component.onCompleted: {
Keys.onEnterPressed: this.clicked() var item = containers.itemAtIndex(containers.currentIndex)
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
} }
}
footer: ColumnLayout {
width: listView.width
spacing: 16
DividerType { DividerType {
Layout.fillWidth: true implicitWidth: parent.width
Layout.leftMargin: 16
Layout.rightMargin: 16
} }
CardType { CardType {
Layout.fillWidth: true implicitWidth: parent.width
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Manual") headerText: qsTr("Manual")
bodyText: qsTr("Choose a VPN protocol") bodyText: qsTr("Choose a VPN protocol")
@@ -141,29 +146,25 @@ PageType {
onClicked: function() { onClicked: function() {
isEasySetup = false isEasySetup = false
checked = true
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
BasicButtonType { BasicButtonType {
id: continueButton id: continueButton
Layout.fillWidth: true implicitWidth: parent.width
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Continue") text: qsTr("Continue")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
if (root.isEasySetup) { if (root.isEasySetup) {
ContainersModel.setProcessedContainerIndex(listView.dockerContainer) ContainersModel.setProcessedContainerIndex(containers.dockerContainer)
PageController.goToPage(PageEnum.PageSetupWizardInstalling) PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.install(listView.dockerContainer, InstallController.install(containers.dockerContainer,
listView.containerDefaultPort, containers.containerDefaultPort,
listView.containerDefaultTransportProto) containers.containerDefaultTransportProto)
} else { } else {
PageController.goToPage(PageEnum.PageSetupWizardProtocols) PageController.goToPage(PageEnum.PageSetupWizardProtocols)
} }
@@ -173,11 +174,9 @@ PageType {
BasicButtonType { BasicButtonType {
id: setupLaterButton id: setupLaterButton
Layout.fillWidth: true implicitWidth: parent.width
Layout.topMargin: 8 anchors.topMargin: 8
Layout.bottomMargin: 24 anchors.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@@ -186,6 +185,9 @@ PageType {
textColor: AmneziaStyle.color.paleGray textColor: AmneziaStyle.color.paleGray
borderWidth: 1 borderWidth: 1
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: fl
visible: { visible: {
if (PageController.isTriggeredByConnectButton()) { if (PageController.isTriggeredByConnectButton()) {
PageController.setTriggeredByConnectButton(false) PageController.setTriggeredByConnectButton(false)
@@ -203,15 +205,5 @@ PageType {
} }
} }
} }
Component.onCompleted: {
var item = listView.itemAtIndex(listView.currentIndex)
if (item !== null) {
var button = item.children[0]
button.checked = true
button.clicked()
}
}
} }
} }
@@ -85,76 +85,92 @@ PageType {
] ]
} }
ListViewType { FlickableType {
id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
currentIndex: -1 Column {
id: content
model: proxyContainersModel anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: ColumnLayout { spacing: 16
width: listView.width
BaseHeaderType { ListView {
Layout.fillWidth: true id: container
Layout.topMargin: 20 width: parent.width
Layout.leftMargin: 16 height: container.contentItem.height
Layout.rightMargin: 16 currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
headerText: qsTr("Installing") delegate: Item {
descriptionText: name implicitWidth: container.width
} implicitHeight: delegateContent.implicitHeight
ProgressBarType { ColumnLayout {
id: progressBar id: delegateContent
Layout.fillWidth: true anchors.fill: parent
Layout.topMargin: 32 anchors.rightMargin: 16
Layout.leftMargin: 16 anchors.leftMargin: 16
Layout.rightMargin: 16
Timer { BaseHeaderType {
id: timer Layout.fillWidth: true
Layout.topMargin: 20
interval: 300 headerText: qsTr("Installing")
repeat: true descriptionText: name
running: root.isTimerRunning }
onTriggered: {
progressBar.value += 0.003 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)
}
}
} }
} }
} }
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,239 +29,256 @@ PageType {
] ]
} }
BackButtonType { FlickableType {
id: backButton anchors.fill: parent
contentHeight: content.height
anchors.top: parent.top Column {
anchors.left: parent.left id: content
anchors.right: parent.right
anchors.topMargin: 20
onFocusChanged: { anchors.top: parent.top
if (this.activeFocus) { anchors.left: parent.left
listView.positionViewAtBeginning() anchors.right: parent.right
}
}
}
ListViewType { ListView {
id: listView id: processedContainerListView
width: parent.width
height: contentItem.height
currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
anchors.top: backButton.bottom property bool isFocusable: true
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
currentIndex: -1 Keys.onTabPressed: {
FocusController.nextKeyTabItem()
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()
} }
}
DrawerType2 { Keys.onBacktabPressed: {
id: showDetailsDrawer FocusController.previousKeyTabItem()
parent: root }
anchors.fill: parent Keys.onUpPressed: {
expandedHeight: parent.height * 0.9 FocusController.nextKeyUpItem()
expandedStateContent: Item { }
implicitHeight: showDetailsDrawer.expandedHeight
BackButtonType { Keys.onDownPressed: {
id: showDetailsBackButton FocusController.nextKeyDownItem()
}
anchors.top: parent.top Keys.onLeftPressed: {
anchors.left: parent.left FocusController.nextKeyLeftItem()
anchors.right: parent.right }
anchors.topMargin: 16
backButtonFunction: function() { Keys.onRightPressed: {
showDetailsDrawer.closeTriggered() 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
} }
}
ListViewType { BaseHeaderType {
id: showDetailsListView id: header
anchors.top: showDetailsBackButton.bottom Layout.fillWidth: true
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
header: ColumnLayout { headerText: qsTr("Installing %1").arg(name)
width: showDetailsListView.width descriptionText: description
}
Header2Type { BasicButtonType {
id: showDetailsDrawerHeader id: showDetailsButton
Layout.fillWidth: true Layout.topMargin: 16
Layout.topMargin: 16 Layout.leftMargin: -8
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: name 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()
} }
} }
model: 1 // fake model to force the ListView to be created without a model DrawerType2 {
id: showDetailsDrawer
parent: root
delegate: ColumnLayout { anchors.fill: parent
width: showDetailsListView.width expandedHeight: parent.height * 0.9
expandedStateContent: Item {
implicitHeight: showDetailsDrawer.expandedHeight
ParagraphTextType { BackButtonType {
Layout.fillWidth: true id: showDetailsBackButton
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: detailedDescription anchors.top: parent.top
textFormat: Text.MarkdownText anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
Rectangle { backButtonFunction: function() {
Layout.fillHeight: true showDetailsDrawer.closeTriggered()
Layout.leftMargin: 16 }
Layout.rightMargin: 16 }
color: AmneziaStyle.color.transparent 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
}
footer: ColumnLayout { ColumnLayout {
width: showDetailsListView.width id: showDetailsDrawerContent
BasicButtonType { anchors.top: parent.top
id: showDetailsCloseButton anchors.left: parent.left
Layout.fillWidth: true anchors.right: parent.right
Layout.bottomMargin: 32 anchors.rightMargin: 16
Layout.leftMargin: 16 anchors.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Close") Header2Type {
id: showDetailsDrawerHeader
Layout.fillWidth: true
Layout.topMargin: 16
clickedFunc: function() { headerText: name
showDetailsDrawer.closeTriggered() }
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()
}
}
}
} }
} }
} }
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
}
} }
} }
} }
@@ -42,21 +42,19 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
ListViewType { ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@@ -74,8 +72,9 @@ PageType {
} }
model: proxyContainersModel model: proxyContainersModel
clip: true
spacing: 0 spacing: 0
reuseItems: true
snapMode: ListView.SnapToItem snapMode: ListView.SnapToItem
delegate: ColumnLayout { delegate: ColumnLayout {
@@ -88,9 +87,9 @@ PageType {
descriptionText: description descriptionText: description
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function () { clickedFunction: function() {
ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index)); ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index))
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings); PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
} }
} }
+29 -45
View File
@@ -13,31 +13,25 @@ import "../Config"
PageType { PageType {
id: root id: root
BackButtonType { FlickableType {
id: backButton id: fl
anchors.top: parent.top 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.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.height
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
}
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@@ -47,13 +41,6 @@ PageType {
headerText: qsTr("Connection key") headerText: qsTr("Connection key")
descriptionText: qsTr("A line that starts with vpn://...") descriptionText: qsTr("A line that starts with vpn://...")
} }
}
spacing: 16
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: textKey id: textKey
@@ -73,26 +60,23 @@ PageType {
} }
} }
} }
}
footer: ColumnLayout { BasicButtonType {
width: listView.width id: continueButton
BasicButtonType { anchors.bottom: parent.bottom
id: continueButton anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 32
Layout.fillWidth: true text: qsTr("Continue")
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.topMargin: 16
Layout.bottomMargin: 32
text: qsTr("Continue") clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textField.text)) {
clickedFunc: function() { PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
if (ImportController.extractConfigFromData(textKey.textField.text)) {
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}
}
} }
} }
} }
@@ -23,12 +23,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
Connections { Connections {
@@ -52,29 +46,27 @@ PageType {
} }
} }
ListViewType { FlickableType {
id: listView id: fl
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right contentHeight: content.implicitHeight + connectButton.implicitHeight
anchors.left: parent.left
header: ColumnLayout { ColumnLayout {
width: listView.width id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType { BaseHeaderType {
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("New connection") headerText: qsTr("New connection")
} }
RowLayout { RowLayout {
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
spacing: 8 spacing: 8
visible: fileName.text !== "" visible: fileName.text !== ""
@@ -96,9 +88,7 @@ PageType {
BasicButtonType { BasicButtonType {
id: showContentButton id: showContentButton
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: -8
Layout.rightMargin: 16
implicitHeight: 32 implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
@@ -109,6 +99,8 @@ PageType {
text: showContent ? qsTr("Collapse content") : qsTr("Show content") text: showContent ? qsTr("Collapse content") : qsTr("Show content")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
showContent = !showContent showContent = !showContent
} }
@@ -116,28 +108,16 @@ PageType {
CheckBoxType { CheckBoxType {
id: cloakingCheckBox id: cloakingCheckBox
objectName: "cloakingCheckBox"
visible: ImportController.isNativeWireGuardConfig() visible: ImportController.isNativeWireGuardConfig()
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.") text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser after have migrated to 6.9
width: listView.width
WarningType { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
textString: ImportController.getMaliciousWarningText() textString: ImportController.getMaliciousWarningText()
textFormat: Qt.RichText textFormat: Qt.RichText
@@ -150,10 +130,8 @@ PageType {
} }
WarningType { WarningType {
Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.fillWidth: true
Layout.rightMargin: 16
textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.") textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.")
@@ -162,10 +140,7 @@ PageType {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 48 Layout.bottomMargin: 48
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: configContent.implicitHeight implicitHeight: configContent.implicitHeight
@@ -186,38 +161,34 @@ PageType {
} }
} }
} }
}
footer: ColumnLayout { Rectangle {
width: listView.width anchors.fill: columnContent
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
BasicButtonType { ColumnLayout {
id: connectButton id: columnContent
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
Layout.fillWidth: true BasicButtonType {
Layout.topMargin: 16 id: connectButton
Layout.bottomMargin: 32 Layout.fillWidth: true
Layout.rightMargin: 16 Layout.bottomMargin: 32
Layout.leftMargin: 16
text: qsTr("Connect") text: qsTr("Connect")
clickedFunc: function() { clickedFunc: function() {
const headerItem = listView.headerItem; if (cloakingCheckBox.checked) {
if (!headerItem) { ImportController.processNativeWireGuardConfig()
console.error("Header item not found in ListView")
return
}
const cloakingCheckBoxItem = listView.findChildWithObjectName(headerItem.children, "cloakingCheckBox");
if (!cloakingCheckBoxItem) {
console.error("cloakingCheckBox not found")
return
}
if (cloakingCheckBoxItem.checked) {
ImportController.processNativeWireGuardConfig()
}
ImportController.importConfig()
} }
ImportController.importConfig()
} }
} }
} }
+35 -9
View File
@@ -15,7 +15,6 @@ import "../Controls2/TextTypes"
import "../Components" import "../Components"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
@@ -43,6 +42,10 @@ PageType {
target: ExportController target: ExportController
function onGenerateConfig(type) { function onGenerateConfig(type) {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.openTriggered()
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
switch (type) { switch (type) {
@@ -52,36 +55,54 @@ PageType {
} }
case PageShare.ConfigType.OpenVpn: { case PageShare.ConfigType.OpenVpn: {
ExportController.generateOpenVpnConfig(clientNameTextField.textField.text) ExportController.generateOpenVpnConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save OpenVPN config")
shareConnectionDrawer.configExtension = ".ovpn"
shareConnectionDrawer.configFileName = "amnezia_for_openvpn"
break break
} }
case PageShare.ConfigType.WireGuard: { case PageShare.ConfigType.WireGuard: {
ExportController.generateWireGuardConfig(clientNameTextField.textField.text) ExportController.generateWireGuardConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save WireGuard config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_wireguard"
break break
} }
case PageShare.ConfigType.Awg: { case PageShare.ConfigType.Awg: {
ExportController.generateAwgConfig(clientNameTextField.textField.text) ExportController.generateAwgConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save AmneziaWG config")
shareConnectionDrawer.configExtension = ".conf"
shareConnectionDrawer.configFileName = "amnezia_for_awg"
break break
} }
case PageShare.ConfigType.ShadowSocks: { case PageShare.ConfigType.ShadowSocks: {
ExportController.generateShadowSocksConfig() ExportController.generateShadowSocksConfig()
shareConnectionDrawer.configCaption = qsTr("Save Shadowsocks config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_shadowsocks"
break break
} }
case PageShare.ConfigType.Cloak: { case PageShare.ConfigType.Cloak: {
ExportController.generateCloakConfig() ExportController.generateCloakConfig()
shareConnectionDrawer.configCaption = qsTr("Save Cloak config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_cloak"
break break
} }
case PageShare.ConfigType.Xray: { case PageShare.ConfigType.Xray: {
ExportController.generateXrayConfig(clientNameTextField.textField.text) ExportController.generateXrayConfig(clientNameTextField.textField.text)
shareConnectionDrawer.configCaption = qsTr("Save XRay config")
shareConnectionDrawer.configExtension = ".json"
shareConnectionDrawer.configFileName = "amnezia_for_xray"
break break
} }
} }
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
PageController.goToPage(PageEnum.PageShareConnection)
} }
function onExportErrorOccurred(error) { function onExportErrorOccurred(error) {
shareConnectionDrawer.closeTriggered()
PageController.showErrorMessage(error) PageController.showErrorMessage(error)
} }
} }
@@ -235,9 +256,6 @@ PageType {
onClicked: { onClicked: {
accessTypeSelector.currentIndex = 0 accessTypeSelector.currentIndex = 0
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
HorizontalRadioButton { HorizontalRadioButton {
@@ -254,9 +272,6 @@ PageType {
ServersModel.getProcessedServerCredentials()) ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
} }
} }
@@ -510,6 +525,9 @@ PageType {
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
parentFlickable: a
clickedFunc: function(){ clickedFunc: function(){
if (clientNameTextField.textField.text !== "") { if (clientNameTextField.textField.text !== "") {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
@@ -808,6 +826,9 @@ PageType {
root.revokeConfig(index) root.revokeConfig(index)
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
// focusItem1.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@@ -821,4 +842,9 @@ PageType {
} }
} }
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
} }
@@ -1,329 +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"
PageType {
id: pageShareConnection
property string headerText
Component.onCompleted: {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
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"
onVisibleChanged: {
configExtension = ".vpn"
configCaption = qsTr("Save AmneziaVPN config")
configFileName = "amnezia_config"
if (visible) {
var serverName = ServersModel.getProcessedServerData("name") || ServersModel.getProcessedServerData("hostName") || "Server"
headerText = qsTr("Connection to ") + serverName
configContentHeaderText = qsTr("File with connection settings to ") + serverName
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
Text {
id: shareHeader
anchors.top: backButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.leftMargin: 16
anchors.rightMargin: 16
text: pageShareConnection.headerText
color: AmneziaStyle.color.paleGray
font.pixelSize: 32
font.weight: 700
font.family: "PT Root UI VF"
wrapMode: Text.WordWrap
}
ListView {
id: listView
anchors.top: shareHeader.bottom
anchors.topMargin: 16
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: pageShareConnection.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: pageShareConnection.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: pageShareConnection.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: pageShareConnection.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: configBackButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() { configContentDrawer.closeTriggered() }
}
FlickableType {
anchors.top: configBackButton.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: pageShareConnection.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: pageShareConnection.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: pageShareConnection.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 = pageShareConnection.isSelfHostedConfig ? ExportController.qrCodesCount : ApiConfigsController.qrCodesCount
if (index >= qrCodesCount) index = 0
parent.source = pageShareConnection.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\"")
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More