Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d26d6d1fb | |||
| 169f11d9c7 | |||
| 816dc3af95 | |||
| b802863de5 | |||
| 8dc2a4b76c | |||
| beb1c6dbf2 | |||
| 3eb06916c7 | |||
| 30d0f84a4f | |||
| 251f2aa5db | |||
| 16d92ddb7c | |||
| e9d4fd8482 | |||
| 9fdcf5ab13 | |||
| a6e6de33c8 | |||
| 53c7fd4d81 | |||
| 2608ea4367 | |||
| d20ed4ad01 | |||
| eae2936449 | |||
| da8ad1f6ba | |||
| 5472347969 |
@@ -358,7 +358,7 @@ jobs:
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '15.4.0'
|
||||
xcode-version: '16.2.0'
|
||||
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
@@ -402,6 +402,67 @@ jobs:
|
||||
path: deploy/build/client/AmneziaVPN.app
|
||||
retention-days: 7
|
||||
|
||||
Build-MacOS-NE:
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
QT_VERSION: 6.8.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 }}
|
||||
|
||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||
DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }}
|
||||
DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }}
|
||||
DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }}
|
||||
FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }}
|
||||
PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }}
|
||||
|
||||
steps:
|
||||
- name: 'Setup xcode'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: '16.2.0'
|
||||
|
||||
- name: 'Install Qt'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
host: 'mac'
|
||||
target: 'desktop'
|
||||
arch: 'clang_64'
|
||||
modules: 'qtremoteobjects qt5compat qtshadertools'
|
||||
dir: ${{ runner.temp }}
|
||||
setup-python: 'true'
|
||||
set-env: 'true'
|
||||
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
|
||||
|
||||
|
||||
- name: 'Get sources'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
fetch-depth: 10
|
||||
|
||||
- name: 'Setup ccache'
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
- name: 'Build project'
|
||||
run: |
|
||||
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin"
|
||||
bash deploy/build_macos_ne.sh
|
||||
|
||||
- name: 'Upload unpacked artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AmneziaVPN_MacOS_unpacked
|
||||
path: deploy/build/client/AmneziaVPN.app
|
||||
retention-days: 7
|
||||
|
||||
# ------------------------------------------------------
|
||||
|
||||
Build-Android:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.9.2)
|
||||
set(AMNEZIAVPN_VERSION 4.8.10.0)
|
||||
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
@@ -12,7 +12,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2092)
|
||||
set(APP_ANDROID_VERSION_CODE 2093)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
@@ -32,13 +32,19 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||
elseif(MACOS_NE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||
else()
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(client)
|
||||
|
||||
if(NOT IOS AND NOT ANDROID)
|
||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||
add_subdirectory(service)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/deploy/installer/config.cmake)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
[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.
|
||||
[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.
|
||||
|
||||
[](https://amnezia.org)
|
||||
|
||||
### [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)
|
||||
### [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)
|
||||
|
||||
> [!TIP]
|
||||
> 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 ).
|
||||
> 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).
|
||||
|
||||
<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/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>
|
||||
<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://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>
|
||||
|
||||
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
[](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
|
||||
|
||||
### [English](https://github.com/amnezia-vpn/amnezia-client/blob/dev/README.md) | Русский
|
||||
[AmneziaVPN](https://amnezia.org) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
[AmneziaVPN](https://amnezia.org?utm_source=github&utm_campaign=amnezia_website-readme-ru) — это open source VPN-клиент, ключевая особенность которого заключается в возможности развернуть собственный VPN на вашем сервере.
|
||||
|
||||
[](https://amnezia.org)
|
||||
|
||||
### [Сайт](https://amnezia.org) | [Зеркало сайта](https://storage.googleapis.com/amnezia/amnezia.org) | [Документация](https://docs.amnezia.org) | [Решение проблем](https://docs.amnezia.org/troubleshooting)
|
||||
### [Сайт](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)
|
||||
|
||||
> [!TIP]
|
||||
> Если [сайт Amnezia](https://amnezia.org) заблокирован в вашем регионе, вы можете воспользоваться [ссылкой на зеркало](https://storage.googleapis.com/amnezia/amnezia.org).
|
||||
> Если [сайт 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).
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
||||
[Все релизы](https://github.com/amnezia-vpn/amnezia-client/releases)
|
||||
|
||||
@@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
set(PROJECT AmneziaVPN)
|
||||
project(${PROJECT})
|
||||
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER "Autogen")
|
||||
set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
||||
@@ -53,6 +52,9 @@ endif()
|
||||
|
||||
qt_standard_project_setup()
|
||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||
)
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||
@@ -110,6 +112,15 @@ include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
if(MACOS_NE)
|
||||
message("MACOS_NE is ON")
|
||||
add_definitions(-DQ_OS_MAC)
|
||||
add_definitions(-DMACOS_NE)
|
||||
message("Add macros for MacOS Network Extension")
|
||||
else()
|
||||
message("MACOS_NE is OFF")
|
||||
endif()
|
||||
|
||||
include_directories(mozilla)
|
||||
include_directories(mozilla/shared)
|
||||
include_directories(mozilla/models)
|
||||
@@ -139,7 +150,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
cmake_policy(SET CMP0099 OLD)
|
||||
cmake_policy(SET CMP0099 NEW)
|
||||
cmake_policy(SET CMP0114 NEW)
|
||||
|
||||
if(NOT BUILD_OSX_APP_IDENTIFIER)
|
||||
@@ -158,7 +169,6 @@ if(APPLE)
|
||||
set(CMAKE_XCODE_GENERATE_SCHEME FALSE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||
|
||||
endif()
|
||||
|
||||
if(LINUX AND NOT ANDROID)
|
||||
@@ -166,8 +176,7 @@ if(LINUX AND NOT ANDROID)
|
||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
message("Client desktop build")
|
||||
if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
||||
add_compile_definitions(AMNEZIA_DESKTOP)
|
||||
endif()
|
||||
|
||||
@@ -178,7 +187,9 @@ endif()
|
||||
if(IOS)
|
||||
include(cmake/ios.cmake)
|
||||
include(cmake/ios-arch-fixup.cmake)
|
||||
elseif(APPLE AND NOT IOS)
|
||||
elseif(APPLE AND MACOS_NE)
|
||||
include(cmake/macos_ne.cmake)
|
||||
elseif(APPLE)
|
||||
include(cmake/osxtools.cmake)
|
||||
include(cmake/macos.cmake)
|
||||
endif()
|
||||
@@ -199,7 +210,7 @@ elseif(APPLE AND NOT IOS)
|
||||
set(DEPLOY_PLATFORM_PATH "macos")
|
||||
endif()
|
||||
|
||||
if(NOT IOS AND NOT ANDROID)
|
||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||
@@ -214,7 +225,6 @@ if(NOT IOS AND NOT ANDROID)
|
||||
$<TARGET_FILE_DIR:${PROJECT}>
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <QTextDocument>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include <QEvent>
|
||||
|
||||
#include "logger.h"
|
||||
#include "ui/controllers/pageController.h"
|
||||
@@ -21,6 +22,8 @@
|
||||
#include "platforms/ios/QRCodeReaderBase.h"
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
#include <QtQuick/QQuickWindow> // for QQuickWindow
|
||||
#include <QWindow> // for qobject_cast<QWindow*>
|
||||
|
||||
AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_CLASS(argc, argv)
|
||||
{
|
||||
@@ -48,8 +51,17 @@ AmneziaApplication::AmneziaApplication(int &argc, char *argv[]) : AMNEZIA_BASE_C
|
||||
|
||||
AmneziaApplication::~AmneziaApplication()
|
||||
{
|
||||
if (m_vpnConnection) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "disconnectFromVpn", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteLater", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
m_vpnConnectionThread.quit();
|
||||
m_vpnConnectionThread.wait(3000);
|
||||
|
||||
if (!m_vpnConnectionThread.wait(5000)) {
|
||||
m_vpnConnectionThread.terminate();
|
||||
m_vpnConnectionThread.wait();
|
||||
}
|
||||
|
||||
if (m_engine) {
|
||||
QObject::disconnect(m_engine, 0, 0, 0);
|
||||
@@ -63,15 +75,28 @@ void AmneziaApplication::init()
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml"));
|
||||
QObject::connect(
|
||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
m_engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[this, url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl) {
|
||||
QCoreApplication::exit(-1);
|
||||
return;
|
||||
}
|
||||
// install filter on main window
|
||||
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
|
||||
win->installEventFilter(this);
|
||||
win->show();
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||
|
||||
#ifdef MACOS_NE
|
||||
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", true);
|
||||
#else
|
||||
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
||||
#endif
|
||||
|
||||
m_vpnConnection.reset(new VpnConnection(m_settings));
|
||||
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
|
||||
m_vpnConnectionThread.start();
|
||||
@@ -167,7 +192,7 @@ bool AmneziaApplication::parseCommands()
|
||||
|
||||
QCommandLineOption c_cleanup { { "c", "cleanup" }, "Cleanup logs" };
|
||||
m_parser.addOption(c_cleanup);
|
||||
|
||||
|
||||
m_parser.process(*this);
|
||||
|
||||
if (m_parser.isSet(c_cleanup)) {
|
||||
@@ -179,9 +204,8 @@ bool AmneziaApplication::parseCommands()
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
void AmneziaApplication::startLocalServer()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
void AmneziaApplication::startLocalServer() {
|
||||
const QString serverName("AmneziaVPNInstance");
|
||||
QLocalServer::removeServer(serverName);
|
||||
|
||||
@@ -198,6 +222,22 @@ void AmneziaApplication::startLocalServer()
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AmneziaApplication::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::Close) {
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
quit();
|
||||
#else
|
||||
if (m_coreController && m_coreController->pageController()) {
|
||||
m_coreController->pageController()->hideMainWindow();
|
||||
}
|
||||
#endif
|
||||
return true; // eat the close
|
||||
}
|
||||
// call base QObject::eventFilter
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
QQmlApplicationEngine *AmneziaApplication::qmlEngine() const
|
||||
{
|
||||
return m_engine;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#include <QGuiApplication>
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#include <QApplication>
|
||||
#endif
|
||||
#include <QClipboard>
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#define amnApp (static_cast<AmneziaApplication *>(QCoreApplication::instance()))
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||
#define AMNEZIA_BASE_CLASS QGuiApplication
|
||||
#else
|
||||
#define AMNEZIA_BASE_CLASS QApplication
|
||||
#define AMNEZIA_BASE_CLASS QApplication
|
||||
#endif
|
||||
|
||||
class AmneziaApplication : public AMNEZIA_BASE_CLASS
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
void loadFonts();
|
||||
bool parseCommands();
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
void startLocalServer();
|
||||
#endif
|
||||
|
||||
@@ -60,6 +60,8 @@ private:
|
||||
QThread m_vpnConnectionThread;
|
||||
|
||||
QNetworkAccessManager *m_nam;
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // AMNEZIA_APPLICATION_H
|
||||
|
||||
@@ -27,9 +27,15 @@ if(WIN32)
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
||||
endif()
|
||||
elseif(APPLE AND NOT IOS)
|
||||
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(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||
if(MACOS_NE)
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
|
||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
|
||||
else()
|
||||
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(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||
endif()
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
||||
|
||||
@@ -14,7 +14,7 @@ set(LIBS ${LIBS}
|
||||
${FW_SECURITY}
|
||||
${FW_COREWLAN}
|
||||
${FW_NETWORK}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
${FW_USER_NOTIFICATIONS}
|
||||
${FW_NETWORK_EXTENSION}
|
||||
)
|
||||
|
||||
@@ -35,6 +35,8 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||
)
|
||||
|
||||
|
||||
|
||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
@@ -53,4 +55,3 @@ execute_process(
|
||||
)
|
||||
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
message("Client ==> MacOS NE build")
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
enable_language(OBJC)
|
||||
enable_language(Swift)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools Widgets)
|
||||
# Link Qt Widgets for QWidget, QMenu, QAction etc.
|
||||
set(LIBS ${LIBS} Qt6::ShaderTools Qt6::Widgets)
|
||||
|
||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||
find_library(FW_AVFOUNDATION AVFoundation)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_STOREKIT StoreKit)
|
||||
find_library(FW_SERVICEMGMT ServiceManagement)
|
||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||
find_library(FW_NETWORKEXTENSION NetworkExtension)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_AUTHENTICATIONSERVICES}
|
||||
${FW_AVFOUNDATION}
|
||||
${FW_FOUNDATION}
|
||||
${FW_STOREKIT}
|
||||
${FW_SERVICEMGMT}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
${FW_NETWORKEXTENSION}
|
||||
)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||
)
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.h PROPERTIES OBJECTIVE_CPP_HEADER TRUE)
|
||||
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ios_controller_wrapper.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
)
|
||||
|
||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE
|
||||
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
|
||||
${Qt6Widgets_PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Info.plist.in
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/app/app.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
XCODE_GENERATE_SCHEME TRUE
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
|
||||
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
|
||||
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"
|
||||
XCODE_EMBED_APP_EXTENSIONS AmneziaVPNNetworkExtension
|
||||
)
|
||||
|
||||
if(DEPLOY)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.AmneziaVPN"
|
||||
)
|
||||
else()
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTEROP_MODE "objcxx"
|
||||
)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_options(${PROJECT} PRIVATE
|
||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||
)
|
||||
|
||||
set(WG_APPLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/amneziawg-apple/Sources)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/LogController.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/ScreenProtection.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/VPNCController.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/macos/app/Images.xcassets
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
add_subdirectory(macos/networkextension)
|
||||
add_dependencies(${PROJECT} AmneziaVPNNetworkExtension)
|
||||
|
||||
get_target_property(QtCore_location Qt6::Core LOCATION)
|
||||
message("QtCore_location")
|
||||
message(${QtCore_location})
|
||||
|
||||
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
|
||||
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
|
||||
|
||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
|
||||
COMMAND /usr/bin/codesign --force --sign "Apple Distribution"
|
||||
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
|
||||
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Signing OpenVPNAdapter framework"
|
||||
)
|
||||
@@ -39,7 +39,7 @@ set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h
|
||||
)
|
||||
|
||||
if(NOT IOS)
|
||||
if(NOT IOS AND NOT MACOS_NE)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.h
|
||||
)
|
||||
@@ -89,12 +89,26 @@ set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||
)
|
||||
|
||||
if(NOT IOS)
|
||||
if(NOT IOS AND NOT MACOS_NE)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/QRCodeReaderBase.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include native macOS platform helpers (dock/status-item)
|
||||
if(APPLE AND NOT IOS)
|
||||
list(APPEND HEADERS
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.h
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.h
|
||||
${CLIENT_ROOT_DIR}/ui/macos_util.h
|
||||
)
|
||||
list(APPEND SOURCES
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macosutils.mm
|
||||
${CLIENT_ROOT_DIR}/platforms/macos/macosstatusicon.mm
|
||||
${CLIENT_ROOT_DIR}/ui/macos_util.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/ui/notificationhandler.cpp
|
||||
|
||||
@@ -131,7 +131,7 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(const QPair<QString,
|
||||
|
||||
// no redirect-gateway
|
||||
} else if (m_settings->routeMode() == Settings::VpnAllExceptSites) {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
config.append("\nredirect-gateway ipv6 !ipv4 bypass-dhcp\n");
|
||||
// Prevent ipv6 leak
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <QTemporaryFile>
|
||||
#include <QThread>
|
||||
#include <qtimer.h>
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
@@ -24,7 +24,7 @@ SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, const QShar
|
||||
|
||||
QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||
{
|
||||
#ifndef Q_OS_IOS
|
||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
@@ -67,9 +67,10 @@ QString SshConfigurator::convertOpenSShKey(const QString &key)
|
||||
#endif
|
||||
}
|
||||
|
||||
// DEAD CODE.
|
||||
void SshConfigurator::openSshTerminal(const ServerCredentials &credentials)
|
||||
{
|
||||
#ifndef Q_OS_IOS
|
||||
#if !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QProcess *p = new QProcess();
|
||||
p->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
|
||||
@@ -101,7 +102,7 @@ QProcessEnvironment SshConfigurator::prepareEnv()
|
||||
pathEnvVar.clear();
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\cygwin;");
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "\\openvpn;");
|
||||
#elif defined(Q_OS_MACX)
|
||||
#elif defined(Q_OS_MACX) && !defined(MACOS_NE)
|
||||
pathEnvVar.prepend(QDir::toNativeSeparators(QApplication::applicationDirPath()) + "/Contents/MacOS");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
return true;
|
||||
|
||||
#elif defined(Q_OS_IOS)
|
||||
// Standard iOS build (without Network Extension limitations)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::OpenVpn: return true;
|
||||
@@ -269,7 +270,23 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
case DockerContainer::Cloak: return true;
|
||||
case DockerContainer::SSXray: return true;
|
||||
// case DockerContainer::ShadowSocks: return true;
|
||||
default: return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif defined(MACOS_NE)
|
||||
// macOS build using Network Extension – hide OpenVPN-based containers
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Awg: return true;
|
||||
case DockerContainer::Xray: return true;
|
||||
case DockerContainer::SSXray: return true;
|
||||
case DockerContainer::OpenVpn:
|
||||
case DockerContainer::Cloak:
|
||||
case DockerContainer::ShadowSocks:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#elif defined(Q_OS_MAC)
|
||||
switch (c) {
|
||||
|
||||
@@ -233,7 +233,7 @@ void CoreController::initSignalHandlers()
|
||||
|
||||
void CoreController::initNotificationHandler()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
m_notificationHandler.reset(NotificationHandler::create(nullptr));
|
||||
|
||||
connect(m_vpnConnection.get(), &VpnConnection::connectionStateChanged, m_notificationHandler.get(),
|
||||
@@ -245,10 +245,10 @@ void CoreController::initNotificationHandler()
|
||||
connect(m_notificationHandler.get(), &NotificationHandler::disconnectRequested, m_connectionController.get(),
|
||||
&ConnectionController::closeConnection);
|
||||
connect(this, &CoreController::translationsUpdated, m_notificationHandler.get(), &NotificationHandler::onTranslationsUpdated);
|
||||
#endif
|
||||
|
||||
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_notificationHandler.get());
|
||||
connect(this, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CoreController::updateTranslator(const QLocale &locale)
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <QQmlContext>
|
||||
#include <QThread>
|
||||
|
||||
#include "ui/systemtray_notificationhandler.h"
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#include "ui/systemtray_notificationhandler.h"
|
||||
#endif
|
||||
|
||||
#include "ui/controllers/api/apiConfigsController.h"
|
||||
#include "ui/controllers/api/apiSettingsController.h"
|
||||
@@ -46,7 +48,7 @@
|
||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||
#include "ui/models/sites_model.h"
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#include "ui/notificationhandler.h"
|
||||
#endif
|
||||
|
||||
@@ -95,7 +97,7 @@ private:
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QSharedPointer<QTranslator> m_translator;
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
QScopedPointer<NotificationHandler> m_notificationHandler;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ ErrorCode GatewayController::get(const QString &endpoint, QByteArray &responseBo
|
||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
request.setUrl(QString(endpoint).arg(m_gatewayEndpoint));
|
||||
request.setUrl(QString(endpoint).arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
||||
|
||||
// bypass killSwitch exceptions for API-gateway
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
@@ -123,7 +123,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
request.setTransferTimeout(m_requestTimeoutMsecs);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
request.setUrl(endpoint.arg(m_gatewayEndpoint));
|
||||
request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl));
|
||||
|
||||
// bypass killSwitch exceptions for API-gateway
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
@@ -344,11 +344,14 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
||||
std::mt19937 generator(randomDevice());
|
||||
std::shuffle(proxyUrls.begin(), proxyUrls.end(), generator);
|
||||
|
||||
QEventLoop wait;
|
||||
QList<QSslError> sslErrors;
|
||||
QByteArray responseBody;
|
||||
|
||||
for (const QString &proxyUrl : proxyUrls) {
|
||||
auto bypassFunction = [this](const QString &endpoint, const QString &proxyUrl, QNetworkReply *reply,
|
||||
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
||||
std::function<bool(QNetworkReply * reply, const QList<QSslError> &sslErrors)> replyProcessingFunction) {
|
||||
QEventLoop wait;
|
||||
QList<QSslError> sslErrors;
|
||||
|
||||
qDebug() << "go to the next proxy endpoint";
|
||||
reply->deleteLater(); // delete the previous reply
|
||||
reply = requestFunction(endpoint.arg(proxyUrl));
|
||||
@@ -358,6 +361,50 @@ void GatewayController::bypassProxy(const QString &endpoint, QNetworkReply *repl
|
||||
wait.exec();
|
||||
|
||||
if (replyProcessingFunction(reply, sslErrors)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (m_proxyUrl.isEmpty()) {
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(1000);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QEventLoop wait;
|
||||
QList<QSslError> sslErrors;
|
||||
QNetworkReply *reply;
|
||||
|
||||
for (const QString &proxyUrl : proxyUrls) {
|
||||
request.setUrl(proxyUrl + "lmbd-health");
|
||||
reply = amnApp->networkManager()->get(request);
|
||||
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||
reply->deleteLater();
|
||||
|
||||
m_proxyUrl = proxyUrl;
|
||||
if (!m_proxyUrl.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_proxyUrl.isEmpty()) {
|
||||
if (bypassFunction(endpoint, m_proxyUrl, reply, requestFunction, replyProcessingFunction)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString &proxyUrl : proxyUrls) {
|
||||
if (bypassFunction(endpoint, proxyUrl, reply, requestFunction, replyProcessingFunction)) {
|
||||
m_proxyUrl = proxyUrl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ private:
|
||||
QString m_gatewayEndpoint;
|
||||
bool m_isDevEnvironment = false;
|
||||
bool m_isStrictKillSwitchEnabled = false;
|
||||
|
||||
inline static QString m_proxyUrl;
|
||||
};
|
||||
|
||||
#endif // GATEWAYCONTROLLER_H
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -390,7 +390,7 @@ QString NetworkUtilities::getGatewayAndIface()
|
||||
close(sock);
|
||||
return gateway_address;
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QString gateway;
|
||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
int afinet_type[] = {AF_INET, AF_INET6};
|
||||
|
||||
@@ -101,10 +101,10 @@ QString InterfaceConfig::toWgConf(const QMap<QString, QString>& extra) const {
|
||||
out << "MTU = " << m_deviceMTU << "\n";
|
||||
}
|
||||
|
||||
if (!m_primaryDnsServer.isNull()) {
|
||||
if (!m_primaryDnsServer.isEmpty()) {
|
||||
QStringList dnsServers;
|
||||
dnsServers.append(m_primaryDnsServer);
|
||||
if (!m_secondaryDnsServer.isNull()) {
|
||||
if (!m_secondaryDnsServer.isEmpty()) {
|
||||
dnsServers.append(m_secondaryDnsServer);
|
||||
}
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 682 B |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -1,6 +1,68 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
"images": [
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "16x16",
|
||||
"scale": "1x",
|
||||
"filename": "16.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "16x16",
|
||||
"scale": "2x",
|
||||
"filename": "16@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "32x32",
|
||||
"scale": "1x",
|
||||
"filename": "32.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "32x32",
|
||||
"scale": "2x",
|
||||
"filename": "32@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "128x128",
|
||||
"scale": "1x",
|
||||
"filename": "128.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "128x128",
|
||||
"scale": "2x",
|
||||
"filename": "128@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "256x256",
|
||||
"scale": "1x",
|
||||
"filename": "256.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "256x256",
|
||||
"scale": "2x",
|
||||
"filename": "256@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "512x512",
|
||||
"scale": "1x",
|
||||
"filename": "512.png"
|
||||
},
|
||||
{
|
||||
"idiom": "mac",
|
||||
"size": "512x512",
|
||||
"scale": "2x",
|
||||
"filename": "512@2x.png"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "xcode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>com.wireguard.ios.app_group_id</key>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict/>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Amnezia VPN config</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>vpn</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>WireGuard config</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>conf</string>
|
||||
<string>cfg</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenVPN config</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ovpn</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>AmneziaVPN backup file</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>backup</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Amnezia VPN config</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.amnezia.AmneziaVPN.amnezia-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.wireguard-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.openvpn-config</string>
|
||||
<string>org.amnezia.AmneziaVPN.backup-config</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSExtensions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.packet-tunnel</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,34 +2,40 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.application-identifier</key>
|
||||
<string>$(DEVELOPMENT_TEAM).$(APP_ID_MACOS)</string>
|
||||
|
||||
<key>com.apple.developer.networking.custom-protocol</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>app-proxy-provider</string>
|
||||
<string>packet-tunnel-provider</string>
|
||||
<string>dns-settings</string>
|
||||
<string>relay</string>
|
||||
<string>content-filter-provider</string>
|
||||
<string>dns-proxy</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.developer.system-extension.install</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.networking.vpn.api</key>
|
||||
<array>
|
||||
<string>allow-vpn</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.developer.team-identifier</key>
|
||||
<string>$(DEVELOPMENT_TEAM)</string>
|
||||
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -2,41 +2,30 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.application-identifier</key>
|
||||
<string>$(DEVELOPMENT_TEAM).$(NETEXT_ID_MACOS)</string>
|
||||
|
||||
<key>com.apple.developer.networking.custom-protocol</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>dns-settings</string>
|
||||
<string>relay</string>
|
||||
<string>packet-tunnel-provider</string>
|
||||
<string>content-filter-provider</string>
|
||||
<string>dns-proxy</string>
|
||||
<string>app-proxy-provider</string>
|
||||
</array>
|
||||
|
||||
<key>keychain-access-groups</key>
|
||||
<key>com.apple.developer.networking.vpn.api</key>
|
||||
<array>
|
||||
<string>$(DEVELOPMENT_TEAM).*</string>
|
||||
<string>allow-vpn</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.developer.team-identifier</key>
|
||||
<string>$(DEVELOPMENT_TEAM)</string>
|
||||
|
||||
<key>com.apple.developer.system-extension.install</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(DEVELOPMENT_TEAM).$(GROUP_ID_MACOS)</string>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.private.network.socket-delegate</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
enable_language(Swift)
|
||||
message("Client message >> macos build >> AmneziaVPNNetworkExtension")
|
||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||
|
||||
add_executable(AmneziaVPNNetworkExtension)
|
||||
|
||||
message("executable_path is: @executable_path/../../Frameworks")
|
||||
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
|
||||
# MACOSX_BUNDLE YES
|
||||
BUNDLE_EXTENSION appex
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPNNetworkExtension"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPNNetworkExtension"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_NAME "${BUILD_IOS_APP_IDENTIFIER}.network-extension"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/AmneziaVPNNetworkExtension.entitlements
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APP_MAJOR_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${BUILD_ID}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPNNetworkExtension"
|
||||
|
||||
XCODE_ATTRIBUTE_APPLICATION_EXTENSION_API_ONLY "YES"
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "11.0"
|
||||
|
||||
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../../../../Frameworks @loader_path/../../../../Frameworks"
|
||||
)
|
||||
|
||||
if(DEPLOY)
|
||||
message("DEPLOY is ON")
|
||||
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "distr macos.org.amnezia.amneziaVPN.NE"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "dev macos.org.amnezia.amneziaVPN.NE"
|
||||
)
|
||||
else()
|
||||
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(AmneziaVPNNetworkExtension PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/WireGuardNetworkExtension-Bridging-Header.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_OPTIMIZATION_LEVEL "-Onone"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
)
|
||||
|
||||
set_target_properties("AmneziaVPNNetworkExtension" PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
|
||||
find_library(FW_ASSETS_LIBRARY AssetsLibrary)
|
||||
find_library(FW_MOBILE_CORE MobileCoreServices)
|
||||
find_library(FW_UI_KIT UIKit)
|
||||
find_library(FW_LIBRESOLV libresolv.9.tbd)
|
||||
|
||||
|
||||
# Set the root directory
|
||||
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
|
||||
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${FW_LIBRESOLV})
|
||||
|
||||
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\")
|
||||
target_compile_options(AmneziaVPNNetworkExtension PRIVATE -DNETWORK_EXTENSION=1)
|
||||
|
||||
set(WG_APPLE_SOURCE_DIR ${CLIENT_ROOT_DIR}/3rd/amneziawg-apple/Sources)
|
||||
|
||||
message("WG_APPLE_SOURCE_DIR is: ${WG_APPLE_SOURCE_DIR}")
|
||||
message("CLIENT_ROOT_DIR is: ${CLIENT_ROOT_DIR}")
|
||||
|
||||
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/WireGuardAdapter.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PacketTunnelSettingsGenerator.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSResolver.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardNetworkExtension/ErrorNotifier.swift
|
||||
${WG_APPLE_SOURCE_DIR}/Shared/Keychain.swift
|
||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
${WG_APPLE_SOURCE_DIR}/Shared/Model/String+ArrayConversion.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/TunnelConfiguration.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddressRange.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Endpoint.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/DNSServer.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/InterfaceConfiguration.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PeerConfiguration.swift
|
||||
${WG_APPLE_SOURCE_DIR}/Shared/FileManager+Extension.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKitC/x25519.c
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/Array+ConcurrentMap.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/IPAddress+AddrInfo.swift
|
||||
${WG_APPLE_SOURCE_DIR}/WireGuardKit/PrivateKey.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/HevSocksTunnel.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/NELogController.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/Log.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/LogRecord.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+WireGuard.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+OpenVPN.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/PacketTunnelProvider+Xray.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/WGConfig.swift
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/iosglue.mm
|
||||
${CLIENT_ROOT_DIR}/platforms/ios/XrayConfig.swift
|
||||
)
|
||||
|
||||
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
## Build wireguard-go-version.h
|
||||
execute_process(
|
||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
||||
)
|
||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a)
|
||||
|
||||
message(${CLIENT_ROOT_DIR})
|
||||
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
||||
@@ -3,27 +3,32 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AmneziaVPNNetworkExtension</string>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>AmneziaVPNNetworkExtension</string>
|
||||
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>org.amnezia.AmneziaVPN.network-extension</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<string>AmneziaVPNNetworkExtension</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<string>${APPLE_PROJECT_VERSION}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>${CMAKE_PROJECT_VERSION_TWEAK}</string>
|
||||
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
|
||||
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AmneziaVPNNetworkExtension</string>
|
||||
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
@@ -31,5 +36,11 @@
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
|
||||
<key>com.wireguard.ios.app_group_id</key>
|
||||
<string>group.org.amnezia.AmneziaVPN</string>
|
||||
|
||||
<key>com.wireguard.macos.app_group_id</key>
|
||||
<string>${BUILD_VPN_DEVELOPMENT_TEAM}.group.org.amnezia.AmneziaVPN</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,10 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macos/gobridge/wireguard.h"
|
||||
|
||||
#include "wireguard-go-version.h"
|
||||
#include "3rd/awg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitGo/wireguard.h"
|
||||
#include "3rd/amneziawg-apple/Sources/WireGuardKitC/WireGuardKitC.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -23,3 +23,8 @@ bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex);
|
||||
bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]);
|
||||
|
||||
void write_msg_to_log(const char* tag, const char* msg);
|
||||
|
||||
// init function definition in C
|
||||
void hev_socks5_tunnel_quit(void);
|
||||
// Updated function definition in C
|
||||
int hev_socks5_tunnel_main(const char* configFile, int fd);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#ifndef WIREGUARD_GO_VERSION
|
||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||
#endif // WIREGUARD_GO_VERSION
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
bool isAnotherInstanceRunning()
|
||||
{
|
||||
QLocalSocket socket;
|
||||
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
AmneziaApplication app(argc, argv);
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
if (isAnotherInstanceRunning()) {
|
||||
QTimer::singleShot(1000, &app, [&]() { app.quit(); });
|
||||
return app.exec();
|
||||
|
||||
@@ -73,7 +73,7 @@ extension PacketTunnelProvider {
|
||||
startHandler = completionHandler
|
||||
ovpnAdapter?.connect(using: packetFlow)
|
||||
}
|
||||
|
||||
|
||||
func handleOpenVPNStatusMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||
guard let completionHandler = completionHandler else { return }
|
||||
let bytesin = ovpnAdapter?.transportStatistics.bytesIn
|
||||
|
||||
@@ -112,9 +112,19 @@ extension PacketTunnelProvider {
|
||||
}
|
||||
}
|
||||
|
||||
let lastHandshakeString = settingsDictionary["last_handshake_time_sec"]
|
||||
let lastHandshake: Int64
|
||||
|
||||
if let lastHandshakeValue = lastHandshakeString, let handshakeValue = Int64(lastHandshakeValue) {
|
||||
lastHandshake = handshakeValue
|
||||
} else {
|
||||
lastHandshake = -2 // Return an error if there is no value for `last_handshake_time_sec`
|
||||
}
|
||||
|
||||
let response: [String: Any] = [
|
||||
"rx_bytes": settingsDictionary["rx_bytes"] ?? "0",
|
||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0"
|
||||
"tx_bytes": settingsDictionary["tx_bytes"] ?? "0",
|
||||
"last_handshake_time_sec": lastHandshake
|
||||
]
|
||||
|
||||
completionHandler(try? JSONSerialization.data(withJSONObject: response, options: []))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !MACOS_NE
|
||||
#include "QRCodeReaderBase.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -108,3 +109,19 @@ void QRCodeReader::startReading() {
|
||||
void QRCodeReader::stopReading() {
|
||||
[m_qrCodeReader stopReading];
|
||||
}
|
||||
#else
|
||||
#include "QRCodeReaderBase.h"
|
||||
|
||||
QRCodeReader::QRCodeReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QRect QRCodeReader::cameraSize() {
|
||||
return QRect();
|
||||
}
|
||||
|
||||
void QRCodeReader::startReading() {}
|
||||
void QRCodeReader::stopReading() {}
|
||||
void QRCodeReader::setCameraSize(QRect) {}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#if !MACOS_NE
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#endif
|
||||
@interface QIOSApplicationDelegate
|
||||
@end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
@implementation QIOSApplicationDelegate (AmneziaVPNDelegate)
|
||||
|
||||
#if !MACOS_NE
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
|
||||
@@ -57,5 +57,5 @@
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#endif
|
||||
@end
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
#if MACOS_NE
|
||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||
|
||||
}
|
||||
|
||||
class ScreenProtection {
|
||||
|
||||
|
||||
}
|
||||
#else
|
||||
import UIKit
|
||||
|
||||
public func toggleScreenshots(_ isEnabled: Bool) {
|
||||
@@ -90,3 +100,4 @@ struct ProtectionPair {
|
||||
textField.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
void disconnectVpn();
|
||||
|
||||
void vpnStatusDidChange(void *pNotification);
|
||||
|
||||
void vpnConfigurationDidChange(void *pNotification);
|
||||
|
||||
void getBackendLogs(std::function<void(const QString &)> &&callback);
|
||||
|
||||
@@ -27,6 +27,7 @@ const char* MessageKey::isOnDemand = "is-on-demand";
|
||||
const char* MessageKey::SplitTunnelType = "SplitTunnelType";
|
||||
const char* MessageKey::SplitTunnelSites = "SplitTunnelSites";
|
||||
|
||||
#if !MACOS_NE
|
||||
static UIViewController* getViewController() {
|
||||
NSArray *windows = [[UIApplication sharedApplication]windows];
|
||||
for (UIWindow *window in windows) {
|
||||
@@ -36,6 +37,7 @@ static UIViewController* getViewController() {
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vpn::ConnectionState iosStatusToState(NEVPNStatus status) {
|
||||
switch (status) {
|
||||
@@ -249,6 +251,21 @@ void IosController::checkStatus()
|
||||
sendVpnExtensionMessage(message, [&](NSDictionary* response){
|
||||
uint64_t txBytes = [response[@"tx_bytes"] intValue];
|
||||
uint64_t rxBytes = [response[@"rx_bytes"] intValue];
|
||||
|
||||
uint64_t last_handshake_time_sec = 0;
|
||||
#if !MACOS_NE
|
||||
if (response[@"last_handshake_time_sec"] && ![response[@"last_handshake_time_sec"] isKindOfClass:[NSNull class]]) {
|
||||
last_handshake_time_sec = [response[@"last_handshake_time_sec"] intValue];
|
||||
} else {
|
||||
qDebug() << "Key last_handshake_time_sec is missing or null";
|
||||
}
|
||||
|
||||
if (last_handshake_time_sec < 0) {
|
||||
disconnectVpn();
|
||||
qDebug() << "Invalid handshake time, disconnecting VPN.";
|
||||
}
|
||||
#endif
|
||||
|
||||
emit bytesChanged(rxBytes - m_rxBytes, txBytes - m_txBytes);
|
||||
m_rxBytes = rxBytes;
|
||||
m_txBytes = txBytes;
|
||||
@@ -803,14 +820,14 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
||||
NSURL *logFileUrl = [[NSURL alloc] initFileURLWithPath:filesToSend[i].toNSString()];
|
||||
[sharingItems addObject:logFileUrl];
|
||||
}
|
||||
|
||||
#if !MACOS_NE
|
||||
UIViewController *qtController = getViewController();
|
||||
if (!qtController) return;
|
||||
|
||||
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
|
||||
|
||||
#endif
|
||||
__block bool isAccepted = false;
|
||||
|
||||
#if !MACOS_NE
|
||||
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||||
isAccepted = completed;
|
||||
emit finished();
|
||||
@@ -823,6 +840,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
||||
popController.sourceRect = CGRectMake(100, 100, 100, 100);
|
||||
}
|
||||
|
||||
#endif
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
@@ -831,6 +849,7 @@ bool IosController::shareText(const QStringList& filesToSend) {
|
||||
}
|
||||
|
||||
QString IosController::openFile() {
|
||||
#if !MACOS_NE
|
||||
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeOpen];
|
||||
|
||||
DocumentPickerDelegate *documentPickerDelegate = [[DocumentPickerDelegate alloc] init];
|
||||
@@ -841,8 +860,9 @@ QString IosController::openFile() {
|
||||
|
||||
[qtController presentViewController:documentPicker animated:YES completion:nil];
|
||||
|
||||
#endif
|
||||
__block QString filePath;
|
||||
|
||||
#if !MACOS_NE
|
||||
documentPickerDelegate.documentPickerClosedCallback = ^(NSString *path) {
|
||||
if (path) {
|
||||
filePath = QString::fromUtf8(path.UTF8String);
|
||||
@@ -851,7 +871,7 @@ QString IosController::openFile() {
|
||||
}
|
||||
emit finished();
|
||||
};
|
||||
|
||||
#endif
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#import <NetworkExtension/NetworkExtension.h>
|
||||
#import <NetworkExtension/NETunnelProviderSession.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if !MACOS_NE
|
||||
#include <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#include <Security/Security.h>
|
||||
|
||||
class IosController;
|
||||
@@ -17,9 +21,10 @@ class IosController;
|
||||
@end
|
||||
|
||||
typedef void (^DocumentPickerClosedCallback)(NSString *path);
|
||||
|
||||
#if !MACOS_NE
|
||||
@interface DocumentPickerDelegate : NSObject <UIDocumentPickerDelegate>
|
||||
|
||||
@property (nonatomic, copy) DocumentPickerClosedCallback documentPickerClosedCallback;
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation DocumentPickerDelegate
|
||||
#if !MACOS_NE
|
||||
@implementation DocumentPickerDelegate
|
||||
|
||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
|
||||
for (NSURL *url in urls) {
|
||||
@@ -42,4 +43,5 @@
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if !MACOS_NE
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface IOSNotificationDelegate
|
||||
@@ -87,3 +89,86 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
||||
}
|
||||
}];
|
||||
}
|
||||
#else
|
||||
|
||||
// Removed the UIResponder and UIApplicationDelegate references as these are not available in macOS
|
||||
@interface IOSNotificationDelegate
|
||||
: NSObject <UNUserNotificationCenterDelegate> {
|
||||
IOSNotificationHandler* m_iosNotificationHandler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation IOSNotificationDelegate
|
||||
|
||||
- (id)initWithObject:(IOSNotificationHandler*)notification {
|
||||
self = [super init]; // Removed `super init` as it refers to UIResponder, which is iOS specific
|
||||
if (self) {
|
||||
m_iosNotificationHandler = notification;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||
willPresentNotification:(UNNotification*)notification
|
||||
withCompletionHandler:
|
||||
(void (^)(UNNotificationPresentationOptions options))completionHandler {
|
||||
Q_UNUSED(center)
|
||||
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse*)response
|
||||
withCompletionHandler:(void (^)())completionHandler {
|
||||
Q_UNUSED(center)
|
||||
Q_UNUSED(response)
|
||||
completionHandler();
|
||||
}
|
||||
@end
|
||||
|
||||
IOSNotificationHandler::IOSNotificationHandler(QObject* parent) : NotificationHandler(parent) {
|
||||
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert |
|
||||
UNAuthorizationOptionBadge)
|
||||
completionHandler:^(BOOL granted, NSError* _Nullable error) {
|
||||
Q_UNUSED(granted);
|
||||
if (!error) {
|
||||
m_delegate = [[IOSNotificationDelegate alloc] initWithObject:this];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
IOSNotificationHandler::~IOSNotificationHandler() { }
|
||||
|
||||
void IOSNotificationHandler::notify(NotificationHandler::Message type, const QString& title,
|
||||
const QString& message, int timerMsec) {
|
||||
Q_UNUSED(type);
|
||||
|
||||
if (!m_delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
||||
content.title = title.toNSString();
|
||||
content.body = message.toNSString();
|
||||
content.sound = [UNNotificationSound defaultSound];
|
||||
|
||||
int timerSec = timerMsec / 1000;
|
||||
UNTimeIntervalNotificationTrigger* trigger =
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||
content:content
|
||||
trigger:trigger];
|
||||
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
center.delegate = (id<UNUserNotificationCenterDelegate>)m_delegate;
|
||||
|
||||
[center addNotificationRequest:request
|
||||
withCompletionHandler:^(NSError* _Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"Local Notification failed");
|
||||
}
|
||||
}];
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -30,7 +30,6 @@ Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent)
|
||||
Ikev2Protocol::~Ikev2Protocol()
|
||||
{
|
||||
qDebug() << "IpsecProtocol::~IpsecProtocol()";
|
||||
disconnect_vpn();
|
||||
Ikev2Protocol::stop();
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ void Ikev2Protocol::stop()
|
||||
{
|
||||
setConnectionState(Vpn::ConnectionState::Disconnecting);
|
||||
{
|
||||
if (! disconnect_vpn() ){
|
||||
if (!disconnect_vpn()){
|
||||
qDebug()<<"We don't disconnect";
|
||||
setConnectionState(Vpn::ConnectionState::Error);
|
||||
}
|
||||
@@ -311,7 +310,9 @@ bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bool Ikev2Protocol::disconnect_vpn(){
|
||||
if ( hRasConn != nullptr ){
|
||||
if ( RasHangUp(hRasConn) != ERROR_SUCCESS)
|
||||
auto ret = RasHangUp(hRasConn);
|
||||
qDebug() << "RasHangUp " << ret;
|
||||
if (ret != ERROR_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
QThread::msleep(3000);
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace amnezia
|
||||
|
||||
constexpr char defaultPort[] = "51820";
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
constexpr char defaultMtu[] = "1280";
|
||||
#else
|
||||
constexpr char defaultMtu[] = "1376";
|
||||
@@ -212,7 +212,7 @@ namespace amnezia
|
||||
namespace awg
|
||||
{
|
||||
constexpr char defaultPort[] = "55424";
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
constexpr char defaultMtu[] = "1280";
|
||||
#else
|
||||
constexpr char defaultMtu[] = "1376";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "core/errorstrings.h"
|
||||
#include "vpnprotocol.h"
|
||||
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
#include "openvpnovercloakprotocol.h"
|
||||
#include "openvpnprotocol.h"
|
||||
#include "shadowsocksvpnprotocol.h"
|
||||
@@ -109,7 +109,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
|
||||
#endif
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) and !defined MACOS_NE || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
|
||||
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
|
||||
case DockerContainer::ShadowSocks: return new ShadowSocksVpnProtocol(configuration);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM alpine:3.15
|
||||
LABEL maintainer="AmneziaVPN"
|
||||
|
||||
ARG XRAY_RELEASE="v1.8.6"
|
||||
ARG XRAY_RELEASE="v25.8.3"
|
||||
|
||||
RUN apk add --no-cache curl unzip bash openssl netcat-openbsd dumb-init rng-tools xz
|
||||
RUN apk --update upgrade --no-cache
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "connectionController.h"
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
@@ -32,8 +32,9 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
||||
|
||||
void ConnectionController::openConnection()
|
||||
{
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
|
||||
{
|
||||
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
@@ -595,7 +595,7 @@ void ImportController::startDecodingQr()
|
||||
m_totalQrCodeChunksCount = 0;
|
||||
m_receivedQrCodeChunksCount = 0;
|
||||
|
||||
#if defined Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
m_isQrCodeProcessed = true;
|
||||
#endif
|
||||
#if defined Q_OS_ANDROID
|
||||
|
||||
@@ -37,7 +37,7 @@ void ListViewFocusController::viewAtCurrentIndex() const
|
||||
}
|
||||
case Section::Delegate: {
|
||||
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
|
||||
Q_ARG(int, 2)); // PositionMode (0 = Visible)
|
||||
Q_ARG(int, 6)); // PositionMode (0 = Beginning; 1 = Center; 2 = End; 3 = Visible; 4 = Contain; 5 = SnapPosition)
|
||||
break;
|
||||
}
|
||||
case Section::Footer: {
|
||||
@@ -112,6 +112,7 @@ void ListViewFocusController::previousDelegate()
|
||||
case Section::Default: {
|
||||
if (hasFooter()) {
|
||||
m_currentSection = Section::Footer;
|
||||
viewAtCurrentIndex();
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
@@ -127,9 +128,11 @@ void ListViewFocusController::previousDelegate()
|
||||
case Section::Delegate: {
|
||||
if (m_delegateIndex > 0) {
|
||||
setDelegateIndex(m_delegateIndex - 1);
|
||||
viewAtCurrentIndex();
|
||||
break;
|
||||
} else if (hasHeader()) {
|
||||
m_currentSection = Section::Header;
|
||||
viewAtCurrentIndex();
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
@@ -137,6 +140,7 @@ void ListViewFocusController::previousDelegate()
|
||||
case Section::Header: {
|
||||
m_isReturnNeeded = true;
|
||||
m_currentSection = Section::Default;
|
||||
viewAtCurrentIndex();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -275,7 +279,7 @@ bool ListViewFocusController::isFirstFocusItemInListView() const
|
||||
return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader();
|
||||
}
|
||||
case Section::Header: {
|
||||
isFirstFocusItemInDelegate();
|
||||
return isFirstFocusItemInDelegate();
|
||||
}
|
||||
case Section::Default: {
|
||||
return true;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include "pageController.h"
|
||||
#include "utils/converter.h"
|
||||
#include "core/errorstrings.h"
|
||||
#if defined(MACOS_NE)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
@@ -25,8 +28,12 @@ PageController::PageController(const QSharedPointer<ServersModel> &serversModel,
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_MACX
|
||||
connect(this, &PageController::raiseMainWindow, []() { setDockIconVisible(true); });
|
||||
connect(this, &PageController::hideMainWindow, []() { setDockIconVisible(false); });
|
||||
connect(this, &PageController::raiseMainWindow, []() {
|
||||
setDockIconVisible(true);
|
||||
});
|
||||
connect(this, &PageController::hideMainWindow, []() {
|
||||
setDockIconVisible(false);
|
||||
});
|
||||
#endif
|
||||
|
||||
connect(this, qOverload<ErrorCode>(&PageController::showErrorMessage), this, &PageController::onShowErrorMessage);
|
||||
@@ -56,14 +63,11 @@ QString PageController::getPagePath(PageLoader::PageEnum page)
|
||||
|
||||
void PageController::closeWindow()
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
// On mobile platforms, quit app on close; on desktop, just hide window
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
qApp->quit();
|
||||
#else
|
||||
if (m_serversModel->getServersCount() == 0) {
|
||||
qApp->quit();
|
||||
} else {
|
||||
emit hideMainWindow();
|
||||
}
|
||||
emit hideMainWindow();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -114,7 +118,7 @@ void PageController::showOnStartup()
|
||||
} else {
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
emit hideMainWindow();
|
||||
#elif defined Q_OS_MACX
|
||||
#elif defined(Q_OS_MACX)
|
||||
setDockIconVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <AmneziaVPN-Swift.h>
|
||||
#endif
|
||||
|
||||
@@ -93,7 +93,7 @@ bool SettingsController::isLoggingEnabled()
|
||||
void SettingsController::toggleLogging(bool enable)
|
||||
{
|
||||
m_settings->setSaveLogs(enable);
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS)
|
||||
AmneziaVPN::toggleLogging(enable);
|
||||
#endif
|
||||
if (enable == true) {
|
||||
@@ -157,8 +157,12 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
{
|
||||
QByteArray data;
|
||||
SystemController::readFile(fileName, data);
|
||||
QFile file(fileName);
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
restoreAppConfigFromData(data);
|
||||
}
|
||||
|
||||
@@ -237,7 +241,7 @@ void SettingsController::clearSettings()
|
||||
|
||||
emit changeSettingsFinished(tr("All settings have been reset to default values"));
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
AmneziaVPN::clearSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include "platforms/ios/ios_controller.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
@@ -58,8 +58,10 @@ void SystemController::saveFile(const QString &fileName, const QString &data)
|
||||
const auto url = fi.absoluteDir().absolutePath();
|
||||
#endif
|
||||
|
||||
#ifndef MACOS_NE
|
||||
QDesktopServices::openUrl(url);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SystemController::readFile(const QString &fileName, QByteArray &data)
|
||||
|
||||
@@ -191,14 +191,14 @@ QJsonObject AwgConfigModel::getConfig()
|
||||
jsonConfig[config_key::junkPacketCount] = m_clientProtocolConfig[config_key::junkPacketCount];
|
||||
jsonConfig[config_key::junkPacketMinSize] = m_clientProtocolConfig[config_key::junkPacketMinSize];
|
||||
jsonConfig[config_key::junkPacketMaxSize] = m_clientProtocolConfig[config_key::junkPacketMaxSize];
|
||||
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1];
|
||||
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2];
|
||||
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3];
|
||||
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4];
|
||||
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5];
|
||||
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1];
|
||||
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2];
|
||||
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3];
|
||||
jsonConfig[config_key::specialJunk1] = m_clientProtocolConfig[config_key::specialJunk1].toString().trimmed();
|
||||
jsonConfig[config_key::specialJunk2] = m_clientProtocolConfig[config_key::specialJunk2].toString().trimmed();
|
||||
jsonConfig[config_key::specialJunk3] = m_clientProtocolConfig[config_key::specialJunk3].toString().trimmed();
|
||||
jsonConfig[config_key::specialJunk4] = m_clientProtocolConfig[config_key::specialJunk4].toString().trimmed();
|
||||
jsonConfig[config_key::specialJunk5] = m_clientProtocolConfig[config_key::specialJunk5].toString().trimmed();
|
||||
jsonConfig[config_key::controlledJunk1] = m_clientProtocolConfig[config_key::controlledJunk1].toString().trimmed();
|
||||
jsonConfig[config_key::controlledJunk2] = m_clientProtocolConfig[config_key::controlledJunk2].toString().trimmed();
|
||||
jsonConfig[config_key::controlledJunk3] = m_clientProtocolConfig[config_key::controlledJunk3].toString().trimmed();
|
||||
jsonConfig[config_key::specialHandshakeTimeout] = m_clientProtocolConfig[config_key::specialHandshakeTimeout];
|
||||
|
||||
m_serverProtocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson());
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "core/controllers/serverController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
#include <AmneziaVPN-Swift.h>
|
||||
#endif
|
||||
|
||||
@@ -782,7 +782,7 @@ void ServersModel::removeApiConfig(const int serverIndex)
|
||||
{
|
||||
auto serverConfig = getServerConfig(serverIndex);
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
QString vpncName = QString("%1 (%2) %3")
|
||||
.arg(serverConfig[config_key::description].toString())
|
||||
.arg(serverConfig[config_key::hostName].toString())
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef NE_NOTIFICATION_HANDLER_H
|
||||
#define NE_NOTIFICATION_HANDLER_H
|
||||
|
||||
#include "notificationhandler.h"
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
class MacOSStatusIcon;
|
||||
|
||||
class NEStatusBarNotificationHandler : public NotificationHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NEStatusBarNotificationHandler(QObject* parent);
|
||||
~NEStatusBarNotificationHandler() override;
|
||||
|
||||
void setConnectionState(Vpn::ConnectionState state) override;
|
||||
void onTranslationsUpdated() override;
|
||||
|
||||
protected:
|
||||
void notify(Message type, const QString& title,
|
||||
const QString& message, int timerMsec) override;
|
||||
|
||||
private:
|
||||
void buildMenu();
|
||||
|
||||
QMenu m_menu;
|
||||
MacOSStatusIcon* m_statusIcon;
|
||||
|
||||
QAction* m_actionShow;
|
||||
QAction* m_actionConnect;
|
||||
QAction* m_actionDisconnect;
|
||||
QAction* m_actionVisitWebsite;
|
||||
QAction* m_actionQuit;
|
||||
};
|
||||
|
||||
#endif // NE_NOTIFICATION_HANDLER_H
|
||||
@@ -11,18 +11,12 @@
|
||||
# include "systemtray_notificationhandler.h"
|
||||
#endif
|
||||
|
||||
|
||||
// static
|
||||
NotificationHandler* NotificationHandler::create(QObject* parent) {
|
||||
#if defined(Q_OS_IOS)
|
||||
return new IOSNotificationHandler(parent);
|
||||
#else
|
||||
|
||||
# if defined(Q_OS_LINUX)
|
||||
//if (LinuxSystemTrayNotificationHandler::requiredCustomImpl()) {
|
||||
// return new LinuxSystemTrayNotificationHandler(parent);
|
||||
//}
|
||||
# endif
|
||||
|
||||
return new SystemTrayNotificationHandler(parent);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ import ProtocolEnum 1.0
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: menuContent
|
||||
|
||||
property var rootWidth
|
||||
@@ -21,13 +20,6 @@ ListView {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
clip: true
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ButtonGroup {
|
||||
id: containersRadioButtonGroup
|
||||
}
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
|
||||
text: qsTr("Split tunneling on the server")
|
||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: siteBasedSplitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: appSplitTunnelingSwitch
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("App-based split tunneling")
|
||||
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: parent.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("Split tunneling")
|
||||
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: splitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
|
||||
text: qsTr("Split tunneling on the server")
|
||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: siteBasedSplitTunnelingSwitch
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Site-based split tunneling")
|
||||
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: appSplitTunnelingSwitch
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("App-based split tunneling")
|
||||
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isAppSplitTinnelingEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ DrawerType2 {
|
||||
headerText: qsTr("Choose application")
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -66,11 +66,6 @@ DrawerType2 {
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: proxyInstalledAppsModel
|
||||
sourceModel: installedAppsModel
|
||||
@@ -81,44 +76,35 @@ DrawerType2 {
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: root.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
RowLayout {
|
||||
CheckBoxType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
CheckBoxType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: appName
|
||||
checked: isAppSelected
|
||||
onCheckedChanged: {
|
||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "image://installedAppImage/" + appIcon
|
||||
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
|
||||
Layout.rightMargin: 48
|
||||
text: appName
|
||||
checked: isAppSelected
|
||||
onCheckedChanged: {
|
||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
Image {
|
||||
source: "image://installedAppImage/" + appIcon
|
||||
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
|
||||
Layout.rightMargin: 48
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ DrawerType2 {
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
@@ -57,14 +57,8 @@ DrawerType2 {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
property bool isFocusable: true
|
||||
property int selectedIndex: LanguageModel.currentLanguageIndex
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
model: LanguageModel
|
||||
|
||||
ButtonGroup {
|
||||
|
||||
@@ -15,7 +15,7 @@ import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
property int selectedIndex: ServersModel.defaultIndex
|
||||
@@ -28,10 +28,6 @@ ListView {
|
||||
|
||||
model: ServersModel
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Connections {
|
||||
target: ServersModel
|
||||
function onDefaultServerIndexChanged(serverIndex) {
|
||||
@@ -39,9 +35,6 @@ ListView {
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
delegate: Item {
|
||||
id: menuContentDelegate
|
||||
objectName: "menuContentDelegate"
|
||||
|
||||
@@ -16,75 +16,61 @@ import "../Controls2/TextTypes"
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
width: parent.width
|
||||
height: root.contentItem.height
|
||||
anchors.fill: parent
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
delegate: ColumnLayout {
|
||||
width: root.width
|
||||
|
||||
property bool isFocusable: false
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: root.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
text: name
|
||||
descriptionText: description
|
||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
clickedFunction: function() {
|
||||
if (isInstalled) {
|
||||
var containerIndex = root.model.mapToSource(index)
|
||||
ContainersModel.setProcessedContainerIndex(containerIndex)
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
LabelWithButtonType {
|
||||
id: containerRadioButton
|
||||
implicitWidth: parent.width
|
||||
|
||||
text: name
|
||||
descriptionText: description
|
||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
if (isInstalled) {
|
||||
var containerIndex = root.model.mapToSource(index)
|
||||
ContainersModel.setProcessedContainerIndex(containerIndex)
|
||||
|
||||
if (serviceType !== ProtocolEnum.Other) {
|
||||
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch (containerIndex) {
|
||||
case ContainerEnum.Ipsec: {
|
||||
if (serviceType !== ProtocolEnum.Other) {
|
||||
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
break
|
||||
return
|
||||
}
|
||||
case ContainerEnum.Dns: {
|
||||
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
||||
InstallController.setShouldCreateServer(false)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
switch (containerIndex) {
|
||||
case ContainerEnum.Ipsec: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||
break
|
||||
}
|
||||
case ContainerEnum.Dns: {
|
||||
PageController.goToPage(PageEnum.PageServiceDnsSettings)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
ProtocolsModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ContainersModel.setProcessedContainerIndex(root.model.mapToSource(index))
|
||||
InstallController.setShouldCreateServer(false)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,220 +1,210 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
||||
property string defaultColor: AmneziaStyle.color.paleGray
|
||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string borderColor: AmneziaStyle.color.paleGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderWidth: 0
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string leftImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageColor: textColor
|
||||
property bool changeLeftImageSize: true
|
||||
|
||||
property bool squareLeftSide: false
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
property var clickedFunc
|
||||
|
||||
property alias buttonTextLabel: buttonText
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitHeight: 56
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: focusBorder
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
radius: 16
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: focusBorder
|
||||
anchors.margins: root.activeFocus ? 2 : 0
|
||||
|
||||
radius: root.activeFocus ? 14 : 16
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
border.color: borderColor
|
||||
border.width: borderWidth
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.squareLeftSide
|
||||
|
||||
z: 1
|
||||
|
||||
width: parent.radius
|
||||
height: parent.radius
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: focusBorder
|
||||
enabled: false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: focusBorder
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: root.leftImageSource
|
||||
visible: root.leftImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: leftImageColor !== "" ? true : false
|
||||
effect: ColorOverlay {
|
||||
color: leftImageColor
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.changeLeftImageSize) {
|
||||
leftImage.Layout.preferredHeight = 20
|
||||
leftImage.Layout.preferredWidth = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
id: buttonText
|
||||
|
||||
color: root.textColor
|
||||
text: root.text
|
||||
visible: root.text === "" ? false : true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
|
||||
source: root.rightImageSource
|
||||
visible: root.rightImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
||||
property string defaultColor: AmneziaStyle.color.paleGray
|
||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string borderColor: AmneziaStyle.color.paleGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderWidth: 0
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string leftImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageColor: textColor
|
||||
property bool changeLeftImageSize: true
|
||||
|
||||
property bool squareLeftSide: false
|
||||
|
||||
property var clickedFunc
|
||||
|
||||
property alias buttonTextLabel: buttonText
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitHeight: 56
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: focusBorder
|
||||
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
radius: 16
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: focusBorder
|
||||
anchors.margins: root.activeFocus ? 2 : 0
|
||||
|
||||
radius: root.activeFocus ? 14 : 16
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
border.color: borderColor
|
||||
border.width: borderWidth
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.squareLeftSide
|
||||
|
||||
z: 1
|
||||
|
||||
width: parent.radius
|
||||
height: parent.radius
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
if (root.pressed) {
|
||||
return pressedColor
|
||||
}
|
||||
return root.hovered ? hoveredColor : defaultColor
|
||||
} else {
|
||||
return disabledColor
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: focusBorder
|
||||
enabled: false
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: focusBorder
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: root.leftImageSource
|
||||
visible: root.leftImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: leftImageColor !== "" ? true : false
|
||||
effect: ColorOverlay {
|
||||
color: leftImageColor
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.changeLeftImageSize) {
|
||||
leftImage.Layout.preferredHeight = 20
|
||||
leftImage.Layout.preferredWidth = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
id: buttonText
|
||||
|
||||
color: root.textColor
|
||||
text: root.text
|
||||
visible: root.text === "" ? false : true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
|
||||
source: root.rightImageSource
|
||||
visible: root.rightImageSource === "" ? false : true
|
||||
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,203 +1,185 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string bodyText
|
||||
property string footerText
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string rightImageSource
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string leftImageSource
|
||||
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property alias focusItem: rightImage
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
|
||||
color: defaultColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
function ensureVisible(item) {
|
||||
if (item.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
ensureVisible(root)
|
||||
}
|
||||
|
||||
focusItem.onFocusChanged: {
|
||||
root.ensureVisible(focusItem)
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: leftImageSource
|
||||
|
||||
visible: leftImageSource !== ""
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 24
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.headerText
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
text: root.bodyText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
textFormat: Text.RichText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
text: root.footerText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
enabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
backgroundRect.color = root.hoveredColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
backgroundRect.color = root.defaultColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string bodyText
|
||||
property string footerText
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
||||
|
||||
property string textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
property string rightImageSource
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string leftImageSource
|
||||
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property alias focusItem: rightImage
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 16
|
||||
|
||||
color: defaultColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
source: leftImageSource
|
||||
|
||||
visible: leftImageSource !== ""
|
||||
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 24
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.headerText
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
text: root.bodyText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
textFormat: Text.RichText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
|
||||
ButtonTextType {
|
||||
text: root.footerText
|
||||
visible: text !== ""
|
||||
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
opacity: root.textOpacity
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
enabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
backgroundRect.color = root.hoveredColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
backgroundRect.color = root.defaultColor
|
||||
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,170 +1,189 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
CheckBox {
|
||||
id: root
|
||||
|
||||
property string descriptionText
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
property string defaultColor: AmneziaStyle.color.transparent
|
||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
|
||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
||||
|
||||
property string imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
property var parentFlickable
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.NoFocus
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
radius: 16
|
||||
|
||||
color: {
|
||||
if (root.hovered) {
|
||||
return hoveredColor
|
||||
}
|
||||
return defaultColor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: imageBorder
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 24
|
||||
height: 24
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.checked ?
|
||||
(root.enabled ?
|
||||
checkedBorderColor :
|
||||
checkedBorderDisabledColor) :
|
||||
defaultBorderColor
|
||||
border.width: 1
|
||||
radius: 4
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
|
||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (root.pressed) {
|
||||
return root.pressedImageColor
|
||||
} else if (root.checked) {
|
||||
if (root.enabled) {
|
||||
return root.checkedImageColor
|
||||
} else {
|
||||
return root.checkedDisabledImageColor
|
||||
}
|
||||
} else {
|
||||
return root.defaultImageColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 8 + background.width
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
spacing: 4
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.descriptionText
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
root.checked = !root.checked
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
CheckBox {
|
||||
id: root
|
||||
|
||||
property string descriptionText
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
property string defaultColor: AmneziaStyle.color.transparent
|
||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
||||
|
||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
||||
|
||||
property string imageSource: "qrc:/images/controls/check.svg"
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.NoFocus
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
radius: 16
|
||||
|
||||
color: {
|
||||
if (root.hovered) {
|
||||
return hoveredColor
|
||||
}
|
||||
return defaultColor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: imageBorder
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 24
|
||||
height: 24
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.color: root.checked ?
|
||||
(root.enabled ?
|
||||
checkedBorderColor :
|
||||
checkedBorderDisabledColor) :
|
||||
defaultBorderColor
|
||||
border.width: 1
|
||||
radius: 4
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
|
||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (root.pressed) {
|
||||
return root.pressedImageColor
|
||||
} else if (root.checked) {
|
||||
if (root.enabled) {
|
||||
return root.checkedImageColor
|
||||
} else {
|
||||
return root.checkedDisabledImageColor
|
||||
}
|
||||
} else {
|
||||
return root.defaultImageColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 8 + background.width
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
spacing: 4
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: root.descriptionText
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => handleSwitch(event)
|
||||
Keys.onReturnPressed: event => handleSwitch(event)
|
||||
Keys.onSpacePressed: event => handleSwitch(event)
|
||||
|
||||
function handleSwitch(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
root.checked = !root.checked
|
||||
root.checkedChanged()
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,328 +1,309 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string text
|
||||
property int textMaximumLineCount: 2
|
||||
property int textElide: Qt.ElideRight
|
||||
|
||||
property string descriptionText
|
||||
|
||||
property var clickedFunction
|
||||
|
||||
property string buttonImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageSource
|
||||
property bool isLeftImageHoverEnabled: true
|
||||
property bool isSmallLeftImage: false
|
||||
|
||||
property alias rightButton: rightImage
|
||||
property alias eyeButton: eyeImage
|
||||
property FlickableType parentFlickable
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
|
||||
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rightImage
|
||||
function onFocusChanged() {
|
||||
if (rightImage.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: leftImageBackground
|
||||
|
||||
visible: leftImageSource ? true : false
|
||||
|
||||
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
|
||||
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
|
||||
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
|
||||
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: leftImageSource
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
property real textLineHeight: 21.6
|
||||
property real descriptionTextLineHeight: 16
|
||||
|
||||
property int textPixelSize: 18
|
||||
property int descriptionTextSize: 13
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.text
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.descriptionColor : root.textColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
maximumLineCount: root.textMaximumLineCount
|
||||
elide: root.textElide
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
function replaceWithAsterisks(input) {
|
||||
return '*'.repeat(input.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: eyeImage
|
||||
visible: buttonImageSource !== ""
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: true
|
||||
image: buttonImageSource
|
||||
imageColor: rightImageColor
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: eyeImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hideDescription = !hideDescription
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
clicked()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
clicked()
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: root
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// property alias focusObjectName: eyeImage.objectName
|
||||
property string text
|
||||
property int textMaximumLineCount: 2
|
||||
property int textElide: Qt.ElideRight
|
||||
|
||||
property string descriptionText
|
||||
|
||||
property var clickedFunction
|
||||
|
||||
property string buttonImageSource
|
||||
property string rightImageSource
|
||||
property string leftImageSource
|
||||
property bool isLeftImageHoverEnabled: true
|
||||
property bool isSmallLeftImage: false
|
||||
|
||||
property alias rightButton: rightImage
|
||||
property alias eyeButton: eyeImage
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property real textOpacity: 1.0
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
|
||||
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: root.enabled
|
||||
|
||||
onEntered: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.hoveredColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.hoveredColor
|
||||
}
|
||||
root.textOpacity = 0.8
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 1
|
||||
}
|
||||
|
||||
onPressedChanged: {
|
||||
if (rightImageSource) {
|
||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
} else if (leftImageSource) {
|
||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
||||
}
|
||||
root.textOpacity = 0.7
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
Rectangle {
|
||||
id: leftImageBackground
|
||||
|
||||
visible: leftImageSource ? true : false
|
||||
|
||||
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
|
||||
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
|
||||
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
|
||||
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Image {
|
||||
id: leftImage
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: leftImageSource
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
property real textLineHeight: 21.6
|
||||
property real descriptionTextLineHeight: 16
|
||||
|
||||
property int textPixelSize: 18
|
||||
property int descriptionTextSize: 13
|
||||
|
||||
ListItemTitleType {
|
||||
text: root.text
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.descriptionColor : root.textColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
maximumLineCount: root.textMaximumLineCount
|
||||
elide: root.textElide
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
||||
color: {
|
||||
if (root.enabled) {
|
||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
||||
} else {
|
||||
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.textOpacity
|
||||
|
||||
visible: root.descriptionText !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
|
||||
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
|
||||
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
function replaceWithAsterisks(input) {
|
||||
return '*'.repeat(input.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: eyeImage
|
||||
visible: buttonImageSource !== ""
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: true
|
||||
image: buttonImageSource
|
||||
imageColor: rightImageColor
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: eyeImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
hideDescription = !hideDescription
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
clicked()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
clicked()
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: rightImage
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
hoverEnabled: false
|
||||
image: rightImageSource
|
||||
imageColor: rightImageColor
|
||||
visible: rightImageSource ? true : false
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
Rectangle {
|
||||
id: rightImageBackground
|
||||
anchors.fill: parent
|
||||
radius: 12
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: root
|
||||
color: AmneziaStyle.color.transparent
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
||||
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (clickedFunction && typeof clickedFunction === "function") {
|
||||
clickedFunction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,33 +6,16 @@ ListView {
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
function findChildWithObjectName(items, name) {
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
if (items[i].objectName === name)
|
||||
return items[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: root
|
||||
|
||||
property var rootWidth
|
||||
@@ -25,13 +25,6 @@ ListView {
|
||||
width: rootWidth
|
||||
height: root.contentItem.height
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
}
|
||||
|
||||
@@ -1,172 +1,162 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
property alias descriptionText: description.text
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string checkedIndicatorColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
|
||||
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
|
||||
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
|
||||
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
|
||||
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
|
||||
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.TabFocus
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
id: switcher
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 52
|
||||
implicitHeight: 32
|
||||
|
||||
radius: 16
|
||||
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
|
||||
: root.defaultIndicatorColor
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
|
||||
: root.defaultIndicatorBorderColor)
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: innerCircle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.checked ? parent.width - width - 4 : 8
|
||||
width: root.checked ? 24 : 16
|
||||
height: root.checked ? 24 : 16
|
||||
radius: 23
|
||||
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
|
||||
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
|
||||
|
||||
Behavior on x {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: innerCircle
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 23
|
||||
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => handleSwitch(event)
|
||||
Keys.onReturnPressed: event => handleSwitch(event)
|
||||
Keys.onSpacePressed: event => handleSwitch(event)
|
||||
|
||||
function handleSwitch(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
root.checked = !root.checked
|
||||
root.checkedChanged()
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
property alias descriptionText: description.text
|
||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property string checkedIndicatorColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorColor: AmneziaStyle.color.transparent
|
||||
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
|
||||
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
|
||||
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
|
||||
|
||||
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
|
||||
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
|
||||
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
|
||||
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
|
||||
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
hoverEnabled: enabled ? true : false
|
||||
focusPolicy: Qt.TabFocus
|
||||
|
||||
indicator: Rectangle {
|
||||
id: switcher
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
implicitWidth: 52
|
||||
implicitHeight: 32
|
||||
|
||||
radius: 16
|
||||
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
|
||||
: root.defaultIndicatorColor
|
||||
|
||||
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
|
||||
: root.defaultIndicatorBorderColor)
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: innerCircle
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.checked ? parent.width - width - 4 : 8
|
||||
width: root.checked ? 24 : 16
|
||||
height: root.checked ? 24 : 16
|
||||
radius: 23
|
||||
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
|
||||
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
|
||||
|
||||
Behavior on x {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: innerCircle
|
||||
width: 40
|
||||
height: 40
|
||||
radius: 23
|
||||
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
|
||||
|
||||
Behavior on color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
ListItemTitleType {
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
text: root.text
|
||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: description
|
||||
|
||||
Layout.fillWidth: true
|
||||
rightPadding: indicator.width
|
||||
|
||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => handleSwitch(event)
|
||||
Keys.onReturnPressed: event => handleSwitch(event)
|
||||
Keys.onSpacePressed: event => handleSwitch(event)
|
||||
|
||||
function handleSwitch(event) {
|
||||
if (!event.isAutoRepeat) {
|
||||
root.checked = !root.checked
|
||||
root.toggled()
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +1,135 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Style 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
interactive: false
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: textArea.implicitHeight
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding: 16
|
||||
leftPadding: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import Style 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
interactive: false
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: textArea.implicitHeight
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding: 16
|
||||
leftPadding: 16
|
||||
anchors.topMargin: 16
|
||||
anchors.bottomMargin: 16
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,180 +1,171 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property string headerText
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string firstButtonImage
|
||||
property string secondButtonImage
|
||||
|
||||
property var firstButtonClickedFunc
|
||||
property var secondButtonClickedFunc
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
property FlickableType parentFlickable: null
|
||||
onFocusChanged: {
|
||||
if (root.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 0
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.headerText
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
leftPadding: 0
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -8
|
||||
spacing: 0
|
||||
ImageButtonType {
|
||||
id: firstButton
|
||||
visible: root.firstButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.firstButtonImage
|
||||
onClicked: function() {
|
||||
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
|
||||
root.firstButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: secondButton
|
||||
visible: root.secondButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.secondButtonImage
|
||||
onClicked: function() {
|
||||
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
|
||||
root.secondButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: resetButton
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
visible: root.textAreaText !== ""
|
||||
image: "qrc:/images/controls/close.svg"
|
||||
|
||||
onClicked: function() {
|
||||
root.textAreaText = ""
|
||||
textArea.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string placeholderText
|
||||
property string text
|
||||
property string headerText
|
||||
property alias textArea: textArea
|
||||
property alias textAreaText: textArea.text
|
||||
|
||||
property string borderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
property string borderNormalColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string firstButtonImage
|
||||
property string secondButtonImage
|
||||
|
||||
property var firstButtonClickedFunc
|
||||
property var secondButtonClickedFunc
|
||||
|
||||
height: 148
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
border.width: 1
|
||||
border.color: getBorderColor(borderNormalColor)
|
||||
radius: 16
|
||||
|
||||
MouseArea {
|
||||
id: parentMouse
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: textArea.forceActiveFocus()
|
||||
hoverEnabled: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 0
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.headerText
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
leftPadding: 0
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
placeholderText: root.placeholderText
|
||||
text: root.text
|
||||
|
||||
onCursorVisibleChanged: {
|
||||
if (textArea.cursorVisible) {
|
||||
fl.interactive = true
|
||||
} else {
|
||||
fl.interactive = false
|
||||
}
|
||||
}
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
MouseArea {
|
||||
id: textAreaMouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
fl.interactive = true
|
||||
contextMenu.open()
|
||||
}
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textArea
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: -8
|
||||
spacing: 0
|
||||
ImageButtonType {
|
||||
id: firstButton
|
||||
visible: root.firstButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.firstButtonImage
|
||||
onClicked: function() {
|
||||
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
|
||||
root.firstButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: secondButton
|
||||
visible: root.secondButtonImage !== ""
|
||||
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
image: root.secondButtonImage
|
||||
onClicked: function() {
|
||||
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
|
||||
root.secondButtonClickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ImageButtonType {
|
||||
id: resetButton
|
||||
imageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
visible: root.textAreaText !== ""
|
||||
image: "qrc:/images/controls/close.svg"
|
||||
|
||||
onClicked: function() {
|
||||
root.textAreaText = ""
|
||||
textArea.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.border.color = getBorderColor(borderFocusedColor)
|
||||
}
|
||||
|
||||
onExited: {
|
||||
root.border.color = getBorderColor(borderNormalColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
root.border.color = getBorderColor(borderHoveredColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getBorderColor(noneFocusedColor) {
|
||||
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,233 +1,220 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string headerTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property alias errorText: errorField.text
|
||||
property bool checkEmptyText: false
|
||||
property bool rightButtonClickedOnEnter: false
|
||||
|
||||
property string buttonText
|
||||
property string buttonImageSource
|
||||
property var clickedFunc
|
||||
|
||||
property alias textField: textField
|
||||
property string textFieldTextColor: AmneziaStyle.color.paleGray
|
||||
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property bool textFieldEditable: true
|
||||
|
||||
property string borderColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string backgroundColor: AmneziaStyle.color.onyxBlack
|
||||
property string backgroundDisabledColor: AmneziaStyle.color.transparent
|
||||
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
property FlickableType parentFlickable
|
||||
|
||||
Connections {
|
||||
target: textField
|
||||
function onFocusChanged() {
|
||||
if (textField.activeFocus) {
|
||||
if (root.parentFlickable) {
|
||||
root.parentFlickable.ensureVisible(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: backgroud
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: input.implicitHeight
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
radius: 16
|
||||
border.color: getBackgroundBorderColor(root.borderColor)
|
||||
border.width: 1
|
||||
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors.fill: backgroud
|
||||
ColumnLayout {
|
||||
Layout.margins: 16
|
||||
LabelTextType {
|
||||
text: root.headerText
|
||||
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
enabled: root.textFieldEditable
|
||||
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
|
||||
|
||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
|
||||
placeholderTextColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: 400
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
height: 24
|
||||
Layout.fillWidth: true
|
||||
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
root.errorText = ""
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.checkEmptyText && text === "") {
|
||||
root.errorText = qsTr("The field can't be empty")
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: contextMenu.open()
|
||||
enabled: true
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textField
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmallTextType {
|
||||
id: errorField
|
||||
|
||||
text: root.errorText
|
||||
visible: root.errorText !== ""
|
||||
color: AmneziaStyle.color.vibrantRed
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: function(mouse) {
|
||||
textField.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
|
||||
}
|
||||
|
||||
|
||||
onExited: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
text: root.buttonText
|
||||
leftImageSource: root.buttonImageSource
|
||||
|
||||
anchors.top: content.top
|
||||
anchors.bottom: content.bottom
|
||||
anchors.right: content.right
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
squareLeftSide: true
|
||||
|
||||
clickedFunc: function() {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundBorderColor(noneFocusedColor) {
|
||||
return textField.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "TextTypes"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string headerText
|
||||
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
|
||||
property string headerTextColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property alias errorText: errorField.text
|
||||
property bool checkEmptyText: false
|
||||
property bool rightButtonClickedOnEnter: false
|
||||
|
||||
property string buttonText
|
||||
property string buttonImageSource
|
||||
property var clickedFunc
|
||||
|
||||
property alias textField: textField
|
||||
property string textFieldTextColor: AmneziaStyle.color.paleGray
|
||||
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
|
||||
|
||||
property bool textFieldEditable: true
|
||||
|
||||
property string borderColor: AmneziaStyle.color.slateGray
|
||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string backgroundColor: AmneziaStyle.color.onyxBlack
|
||||
property string backgroundDisabledColor: AmneziaStyle.color.transparent
|
||||
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: backgroud
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: input.implicitHeight
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
radius: 16
|
||||
border.color: getBackgroundBorderColor(root.borderColor)
|
||||
border.width: 1
|
||||
|
||||
Behavior on border.color {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors.fill: backgroud
|
||||
ColumnLayout {
|
||||
Layout.margins: 16
|
||||
LabelTextType {
|
||||
text: root.headerText
|
||||
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
|
||||
|
||||
visible: text !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
enabled: root.textFieldEditable
|
||||
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
|
||||
|
||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
|
||||
|
||||
placeholderTextColor: AmneziaStyle.color.charcoalGray
|
||||
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: 400
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
height: 24
|
||||
Layout.fillWidth: true
|
||||
|
||||
topPadding: 0
|
||||
rightPadding: 0
|
||||
leftPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
root.errorText = ""
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.checkEmptyText && text === "") {
|
||||
root.errorText = qsTr("The field can't be empty")
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: contextMenu.open()
|
||||
enabled: true
|
||||
}
|
||||
|
||||
ContextMenuType {
|
||||
id: contextMenu
|
||||
textObj: textField
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmallTextType {
|
||||
id: errorField
|
||||
|
||||
text: root.errorText
|
||||
visible: root.errorText !== ""
|
||||
color: AmneziaStyle.color.vibrantRed
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: function(mouse) {
|
||||
textField.forceActiveFocus()
|
||||
mouse.accepted = false
|
||||
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
|
||||
}
|
||||
|
||||
|
||||
onExited: {
|
||||
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
|
||||
|
||||
focusPolicy: Qt.NoFocus
|
||||
text: root.buttonText
|
||||
leftImageSource: root.buttonImageSource
|
||||
|
||||
anchors.top: content.top
|
||||
anchors.bottom: content.bottom
|
||||
anchors.right: content.right
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
squareLeftSide: true
|
||||
|
||||
clickedFunc: function() {
|
||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
root.clickedFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundBorderColor(noneFocusedColor) {
|
||||
return textField.focus ? root.borderFocusedColor : noneFocusedColor
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
|
||||
clickedFunc()
|
||||
}
|
||||
|
||||
// if (KeyNavigation.tab) {
|
||||
// KeyNavigation.tab.forceActiveFocus();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ PageType {
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: proxyServersModel
|
||||
|
||||
sourceModel: ServersModel
|
||||
|
||||
filters: [
|
||||
ValueFilter {
|
||||
roleName: "isCurrentlyProcessed"
|
||||
@@ -29,67 +31,55 @@ PageType {
|
||||
]
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
spacing: 16
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: proxyServersModel
|
||||
|
||||
spacing: 16
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
Repeater {
|
||||
model: proxyServersModel
|
||||
delegate: Item {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
headerText: qsTr("Removing services from %1").arg(name)
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Removing services from %1").arg(name)
|
||||
}
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
ProgressBarType {
|
||||
id: progressBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
text: qsTr("Usually it takes no more than 5 minutes")
|
||||
}
|
||||
interval: 300
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
progressBar.value += 0.003
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Usually it takes no more than 5 minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,23 +25,17 @@ PageType {
|
||||
anchors.topMargin: 20
|
||||
}
|
||||
|
||||
ListView {
|
||||
ListViewType {
|
||||
id: listView
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
ScrollBar.vertical: ScrollBarType {}
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
@@ -50,16 +44,14 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
model: 1
|
||||
clip: true
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
spacing: 16
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
@@ -87,8 +79,6 @@ PageType {
|
||||
width: listView.width
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -16,349 +16,397 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: saveButton.top
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
width: parent.width
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: AwgConfigModel
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === ""
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
spacing: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
KeyNavigation.tab: junkPacketCountTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
headerText: "Jc - Junk packet count"
|
||||
textField.text: clientJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketCount) {
|
||||
clientJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: junkPacketMinSizeTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
headerText: "Jmin - Junk packet minimum size"
|
||||
textField.text: clientJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMinSize) {
|
||||
clientJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
headerText: "Jmax - Junk packet maximum size"
|
||||
textField.text: clientJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMaxSize) {
|
||||
clientJunkPacketMaxSize = textField.text
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk1TextField
|
||||
headerText: qsTr("I1 - First special junk packet")
|
||||
textField.text: clientSpecialJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk1) {
|
||||
clientSpecialJunk1 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jc - Junk packet count"
|
||||
textField.text: clientJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketCount) {
|
||||
clientJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk2TextField
|
||||
headerText: qsTr("I2 - Second special junk packet")
|
||||
textField.text: clientSpecialJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk2) {
|
||||
clientSpecialJunk2 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jmin - Junk packet minimum size"
|
||||
textField.text: clientJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMinSize) {
|
||||
clientJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk3TextField
|
||||
headerText: qsTr("I3 - Third special junk packet")
|
||||
textField.text: clientSpecialJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk3) {
|
||||
clientSpecialJunk3 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "Jmax - Junk packet maximum size"
|
||||
textField.text: clientJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientJunkPacketMaxSize) {
|
||||
clientJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk4TextField
|
||||
headerText: qsTr("I4 - Fourth special junk packet")
|
||||
textField.text: clientSpecialJunk4
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk1TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk4) {
|
||||
clientSpecialJunk4 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I1 - First special junk packet")
|
||||
textField.text: clientSpecialJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk1) {
|
||||
clientSpecialJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: specialJunk5TextField
|
||||
headerText: qsTr("I5 - Fifth special junk packet")
|
||||
textField.text: clientSpecialJunk5
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk2TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk5 ) {
|
||||
clientSpecialJunk5 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I2 - Second special junk packet")
|
||||
textField.text: clientSpecialJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk2) {
|
||||
clientSpecialJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk1TextField
|
||||
headerText: qsTr("J1 - First controlled junk packet")
|
||||
textField.text: clientControlledJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk3TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk1) {
|
||||
clientControlledJunk1 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I3 - Third special junk packet")
|
||||
textField.text: clientSpecialJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk3) {
|
||||
clientSpecialJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk2TextField
|
||||
headerText: qsTr("J2 - Second controlled junk packet")
|
||||
textField.text: clientControlledJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk4TextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk2) {
|
||||
clientControlledJunk2 = textField.text
|
||||
}
|
||||
headerText: qsTr("I4 - Fourth special junk packet")
|
||||
textField.text: clientSpecialJunk4
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk4) {
|
||||
clientSpecialJunk4 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: controlledJunk3TextField
|
||||
headerText: qsTr("J3 - Third controlled junk packet")
|
||||
textField.text: clientControlledJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: specialJunk5TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk3) {
|
||||
clientControlledJunk3 = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("I5 - Fifth special junk packet")
|
||||
textField.text: clientSpecialJunk5
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialJunk5 ) {
|
||||
clientSpecialJunk5 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: iTimeTextField
|
||||
headerText: qsTr("Itime - Special handshake timeout")
|
||||
textField.text: clientSpecialHandshakeTimeout
|
||||
checkEmptyText: false
|
||||
AwgTextField {
|
||||
id: controlledJunk1TextField
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialHandshakeTimeout) {
|
||||
clientSpecialHandshakeTimeout = textField.text
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J1 - First controlled junk packet")
|
||||
textField.text: clientControlledJunk1
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk1) {
|
||||
clientControlledJunk1 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
AwgTextField {
|
||||
id: controlledJunk2TextField
|
||||
|
||||
text: qsTr("Server settings")
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J2 - Second controlled junk packet")
|
||||
textField.text: clientControlledJunk2
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk2) {
|
||||
clientControlledJunk2 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: portTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: controlledJunk3TextField
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("J3 - Third controlled junk packet")
|
||||
textField.text: clientControlledJunk3
|
||||
textField.validator: null
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientControlledJunk3) {
|
||||
clientControlledJunk3 = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: iTimeTextField
|
||||
|
||||
headerText: "S1 - Init packet junk size"
|
||||
textField.text: serverInitPacketJunkSize
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Itime - Special handshake timeout")
|
||||
textField.text: clientSpecialHandshakeTimeout
|
||||
checkEmptyText: false
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientSpecialHandshakeTimeout) {
|
||||
clientSpecialHandshakeTimeout = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
enabled: false
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: "S2 - Response packet junk size"
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
}
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
// enabled: false
|
||||
AwgTextField {
|
||||
id: portTextField
|
||||
|
||||
// headerText: "S3 - Cookie Reply packet junk size"
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
// }
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
// enabled: false
|
||||
enabled: false
|
||||
|
||||
// headerText: "S4 - Transport packet junk size"
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
// }
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
|
||||
headerText: "H1 - Init packet magic header"
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
enabled: false
|
||||
enabled: false
|
||||
|
||||
headerText: "H2 - Response packet magic header"
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
}
|
||||
headerText: "S1 - Init packet junk size"
|
||||
textField.text: serverInitPacketJunkSize
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
|
||||
headerText: "H3 - Underload packet magic header"
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
}
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
enabled: false
|
||||
enabled: false
|
||||
|
||||
headerText: "H4 - Transport packet magic header"
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
}
|
||||
headerText: "S2 - Response packet junk size"
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// enabled: false
|
||||
|
||||
// headerText: "S3 - Cookie Reply packet junk size"
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// enabled: false
|
||||
|
||||
// headerText: "S4 - Transport packet junk size"
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H1 - Init packet magic header"
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H2 - Response packet magic header"
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H3 - Underload packet magic header"
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: "H4 - Transport packet magic header"
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,18 +423,17 @@ PageType {
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
enabled: listview.currentItem.isSaveButtonEnabled
|
||||
enabled: listView.currentItem.isSaveButtonEnabled
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listview.positionViewAtEnd()
|
||||
listView.positionViewAtEnd()
|
||||
}
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
@@ -401,11 +448,9 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,334 +19,343 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
property bool isFocusable: true
|
||||
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
width: parent.width
|
||||
|
||||
Keys.onTabPressed: {
|
||||
FocusController.nextKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onBacktabPressed: {
|
||||
FocusController.previousKeyTabItem()
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
FocusController.nextKeyUpItem()
|
||||
}
|
||||
|
||||
Keys.onDownPressed: {
|
||||
FocusController.nextKeyDownItem()
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: {
|
||||
FocusController.nextKeyLeftItem()
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
FocusController.nextKeyRightItem()
|
||||
}
|
||||
|
||||
clip: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
model: AwgConfigModel
|
||||
|
||||
delegate: Item {
|
||||
delegate: ColumnLayout {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
|
||||
width: listView.width
|
||||
|
||||
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
spacing: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("AmneziaWG settings")
|
||||
}
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
headerText: qsTr("Jc - Junk packet count")
|
||||
textField.text: serverJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketCount) {
|
||||
serverJunkPacketCount = textField.text
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
headerText: qsTr("Jmin - Junk packet minimum size")
|
||||
textField.text: serverJunkPacketMinSize
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMinSize) {
|
||||
serverJunkPacketMinSize = textField.text
|
||||
}
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
headerText: qsTr("Jmax - Junk packet maximum size")
|
||||
textField.text: serverJunkPacketMaxSize
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMaxSize) {
|
||||
serverJunkPacketMaxSize = textField.text
|
||||
}
|
||||
AwgTextField {
|
||||
id: junkPacketCountTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jc - Junk packet count")
|
||||
textField.text: serverJunkPacketCount
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketCount) {
|
||||
serverJunkPacketCount = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMinSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jmin - Junk packet minimum size")
|
||||
textField.text: serverJunkPacketMinSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMinSize) {
|
||||
serverJunkPacketMinSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: junkPacketMaxSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Jmax - Junk packet maximum size")
|
||||
textField.text: serverJunkPacketMaxSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverJunkPacketMaxSize) {
|
||||
serverJunkPacketMaxSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("S1 - Init packet junk size")
|
||||
textField.text: serverInitPacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketJunkSize) {
|
||||
serverInitPacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("S2 - Response packet junk size")
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketJunkSize) {
|
||||
serverResponsePacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// headerText: qsTr("S3 - Cookie reply packet junk size")
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverCookieReplyPacketJunkSize) {
|
||||
// serverCookieReplyPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
|
||||
// Layout.leftMargin: 16
|
||||
// Layout.rightMargin: 16
|
||||
|
||||
// headerText: qsTr("S4 - Transport packet junk size")
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverTransportPacketJunkSize) {
|
||||
// serverTransportPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H1 - Init packet magic header")
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketMagicHeader) {
|
||||
serverInitPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H2 - Response packet magic header")
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketMagicHeader) {
|
||||
serverResponsePacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H3 - Underload packet magic header")
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverUnderloadPacketMagicHeader) {
|
||||
serverUnderloadPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("H4 - Transport packet magic header")
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverTransportPacketMagicHeader) {
|
||||
serverTransportPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveRestartButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
|
||||
transportPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketMagicHeaderTextField.errorText === "" &&
|
||||
initPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketJunkSizeTextField.errorText === "" &&
|
||||
// cookieReplyHeaderJunkTextField.errorText === "" &&
|
||||
// transportHeaderJunkTextField.errorText === "" &&
|
||||
initPacketJunkSizeTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === "" &&
|
||||
portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listView.positionViewAtEnd()
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketJunkSizeTextField
|
||||
headerText: qsTr("S1 - Init packet junk size")
|
||||
textField.text: serverInitPacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketJunkSize) {
|
||||
serverInitPacketJunkSize = textField.text
|
||||
clickedFunc: function() {
|
||||
if (delegateItem.isEnabled) {
|
||||
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
|
||||
transportPacketMagicHeaderTextField.textField.text,
|
||||
responsePacketMagicHeaderTextField.textField.text,
|
||||
initPacketMagicHeaderTextField.textField.text)) {
|
||||
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketJunkSizeTextField
|
||||
headerText: qsTr("S2 - Response packet junk size")
|
||||
textField.text: serverResponsePacketJunkSize
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketJunkSize) {
|
||||
serverResponsePacketJunkSize = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AwgTextField {
|
||||
// id: cookieReplyPacketJunkSizeTextField
|
||||
// headerText: qsTr("S3 - Cookie reply packet junk size")
|
||||
// textField.text: serverCookieReplyPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverCookieReplyPacketJunkSize) {
|
||||
// serverCookieReplyPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// AwgTextField {
|
||||
// id: transportPacketJunkSizeTextField
|
||||
// headerText: qsTr("S4 - Transport packet junk size")
|
||||
// textField.text: serverTransportPacketJunkSize
|
||||
|
||||
// textField.onEditingFinished: {
|
||||
// if (textField.text !== serverTransportPacketJunkSize) {
|
||||
// serverTransportPacketJunkSize = textField.text
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
AwgTextField {
|
||||
id: initPacketMagicHeaderTextField
|
||||
headerText: qsTr("H1 - Init packet magic header")
|
||||
textField.text: serverInitPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverInitPacketMagicHeader) {
|
||||
serverInitPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: responsePacketMagicHeaderTextField
|
||||
headerText: qsTr("H2 - Response packet magic header")
|
||||
textField.text: serverResponsePacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverResponsePacketMagicHeader) {
|
||||
serverResponsePacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: underloadPacketMagicHeaderTextField
|
||||
headerText: qsTr("H3 - Underload packet magic header")
|
||||
textField.text: serverUnderloadPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverUnderloadPacketMagicHeader) {
|
||||
serverUnderloadPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AwgTextField {
|
||||
id: transportPacketMagicHeaderTextField
|
||||
headerText: qsTr("H4 - Transport packet magic header")
|
||||
textField.text: serverTransportPacketMagicHeader
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== serverTransportPacketMagicHeader) {
|
||||
serverTransportPacketMagicHeader = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BasicButtonType {
|
||||
id: saveRestartButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
|
||||
transportPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketMagicHeaderTextField.errorText === "" &&
|
||||
initPacketMagicHeaderTextField.errorText === "" &&
|
||||
responsePacketJunkSizeTextField.errorText === "" &&
|
||||
// cookieReplyHeaderJunkTextField.errorText === "" &&
|
||||
// transportHeaderJunkTextField.errorText === "" &&
|
||||
initPacketJunkSizeTextField.errorText === "" &&
|
||||
junkPacketMaxSizeTextField.errorText === "" &&
|
||||
junkPacketMinSizeTextField.errorText === "" &&
|
||||
junkPacketCountTextField.errorText === "" &&
|
||||
portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
listview.positionViewAtEnd()
|
||||
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
parseInt(responsePacketJunkSizeTextField.textField.text))) {
|
||||
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
|
||||
return
|
||||
}
|
||||
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(responsePacketJunkSizeTextField.textField.text),
|
||||
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(transportPacketJunkSizeTextField.textField.text))) {
|
||||
// PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)"))
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
if (delegateItem.isEnabled) {
|
||||
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
|
||||
transportPacketMagicHeaderTextField.textField.text,
|
||||
responsePacketMagicHeaderTextField.textField.text,
|
||||
initPacketMagicHeaderTextField.textField.text)) {
|
||||
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
|
||||
return
|
||||
}
|
||||
|
||||
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
parseInt(responsePacketJunkSizeTextField.textField.text))) {
|
||||
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
|
||||
return
|
||||
}
|
||||
// if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(responsePacketJunkSizeTextField.textField.text),
|
||||
// parseInt(cookieReplyPacketJunkSizeTextField.textField.text),
|
||||
// parseInt(transportPacketJunkSizeTextField.textField.text))) {
|
||||
// PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92) + S3 + cookie reply size (64) + S4 + transport packet size (32)"))
|
||||
// return
|
||||
// }
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveRestartButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(AwgConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,212 +16,192 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Column {
|
||||
id: content
|
||||
property int selectedIndex: 0
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property int selectedIndex: 0
|
||||
headerText: qsTr("Cloak settings")
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
model: CloakConfigModel
|
||||
|
||||
clip: true
|
||||
reuseItems: true
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
model: CloakConfigModel
|
||||
property alias trafficFromField: trafficFromField
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
spacing: 0
|
||||
|
||||
property alias trafficFromField: trafficFromField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
TextFieldWithHeaderType {
|
||||
id: trafficFromField
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Cloak settings")
|
||||
var indexHttps = tmpText.indexOf("https://")
|
||||
if (indexHttps === 0) {
|
||||
tmpText = textField.text.substring(8)
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: trafficFromField
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
var indexHttps = tmpText.indexOf("https://")
|
||||
if (indexHttps === 0) {
|
||||
tmpText = textField.text.substring(8)
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
selectedIndex = i
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
selectedIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: trafficFromField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(CloakConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: trafficFromField.errorText === "" &&
|
||||
portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(CloakConfigModel.getConfig())
|
||||
}
|
||||
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,428 +17,414 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(backButton.enabled && backButton.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
headerText: qsTr("OpenVPN Settings")
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
model: OpenVpnConfigModel
|
||||
|
||||
model: OpenVpnConfigModel
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
spacing: 0
|
||||
|
||||
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
enabled: listView.enabled
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("OpenVPN settings")
|
||||
}
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
text: qsTr("Network protocol")
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
rootWidth: root.width
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
enabled: isTransportProtoEditable
|
||||
|
||||
parentFlickable: fl
|
||||
currentIndex: {
|
||||
return transportProto === "tcp" ? 1 : 0
|
||||
}
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
onCurrentIndexChanged: {
|
||||
if (transportProto === "tcp" && currentIndex === 0) {
|
||||
transportProto = "udp"
|
||||
} else if (transportProto === "udp" && currentIndex === 1) {
|
||||
transportProto = "tcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
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
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: autoNegotiateEncryprionSwitcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Auto-negotiate encryption")
|
||||
checked: autoNegotiateEncryprion
|
||||
|
||||
onToggled: function() {
|
||||
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
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
text: qsTr("Network protocol")
|
||||
}
|
||||
|
||||
TransportProtoSelector {
|
||||
id: transportProtoSelector
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
rootWidth: root.width
|
||||
|
||||
enabled: isTransportProtoEditable
|
||||
|
||||
currentIndex: {
|
||||
return transportProto === "tcp" ? 1 : 0
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (transportProto === "tcp" && currentIndex === 0) {
|
||||
transportProto = "udp"
|
||||
} else if (transportProto === "udp" && currentIndex === 1) {
|
||||
transportProto = "tcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
parentFlickable: fl
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: autoNegotiateEncryprionSwitcher
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Auto-negotiate encryption")
|
||||
checked: autoNegotiateEncryprion
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== autoNegotiateEncryprion) {
|
||||
autoNegotiateEncryprion = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: hashDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
descriptionText: qsTr("Hash")
|
||||
headerText: qsTr("Hash")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
id: hashListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : qsTr("SHA512") }
|
||||
ListElement { name : qsTr("SHA384") }
|
||||
ListElement { name : qsTr("SHA256") }
|
||||
ListElement { name : qsTr("SHA3-512") }
|
||||
ListElement { name : qsTr("SHA3-384") }
|
||||
ListElement { name : qsTr("SHA3-256") }
|
||||
ListElement { name : qsTr("whirlpool") }
|
||||
ListElement { name : qsTr("BLAKE2b512") }
|
||||
ListElement { name : qsTr("BLAKE2s256") }
|
||||
ListElement { name : qsTr("SHA1") }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
hashDropDown.text = selectedText
|
||||
hash = hashDropDown.text
|
||||
hashDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
hashDropDown.text = hash
|
||||
|
||||
for (var i = 0; i < hashListView.model.count; i++) {
|
||||
if (hashListView.model.get(i).name === hashDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.preferredHeight: checkboxLayout.implicitHeight
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
radius: 16
|
||||
|
||||
Connections {
|
||||
target: tlsAuthCheckBox
|
||||
enabled: !GC.isMobile()
|
||||
|
||||
function onFocusChanged() {
|
||||
if (tlsAuthCheckBox.activeFocus) {
|
||||
fl.ensureVisible(contentRect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: checkboxLayout
|
||||
|
||||
anchors.fill: parent
|
||||
CheckBoxType {
|
||||
id: tlsAuthCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("TLS auth")
|
||||
checked: tlsAuth
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== tlsAuth) {
|
||||
console.log("tlsAuth changed to: " + checked)
|
||||
tlsAuth = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
CheckBoxType {
|
||||
id: blockDnsCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Block DNS requests outside of VPN")
|
||||
checked: blockDns
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== blockDns) {
|
||||
blockDns = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: additionalClientCommandsSwitcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
parentFlickable: fl
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: !autoNegotiateEncryprionSwitcher.checked
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
Layout.preferredHeight: checkboxLayout.implicitHeight
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
radius: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: checkboxLayout
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
CheckBoxType {
|
||||
id: tlsAuthCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("TLS auth")
|
||||
checked: tlsAuth
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== tlsAuth) {
|
||||
console.log("tlsAuth changed to: " + checked)
|
||||
tlsAuth = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
CheckBoxType {
|
||||
id: blockDnsCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Block DNS requests outside of VPN")
|
||||
checked: blockDns
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked !== blockDns) {
|
||||
blockDns = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: additionalClientCommandsSwitcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
checked: additionalClientCommands !== ""
|
||||
|
||||
text: qsTr("Additional client configuration commands")
|
||||
|
||||
onToggled: function() {
|
||||
if (!checked) {
|
||||
additionalClientCommands = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextAreaType {
|
||||
id: additionalClientCommandsTextArea
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: additionalClientCommandsSwitcher.checked
|
||||
|
||||
textAreaText: additionalClientCommands
|
||||
placeholderText: qsTr("Commands:")
|
||||
|
||||
textArea.onEditingFinished: {
|
||||
if (additionalClientCommands !== textAreaText) {
|
||||
additionalClientCommands = textAreaText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: additionalServerCommandsSwitcher
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
checked: additionalServerCommands !== ""
|
||||
|
||||
text: qsTr("Additional server configuration commands")
|
||||
|
||||
onToggled: function() {
|
||||
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);
|
||||
InstallController.updateContainer(OpenVpnConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,164 +19,154 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: header.bottom
|
||||
anchors.left: parent.left
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
contentHeight: content.height
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 32
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: parent.width
|
||||
height: contentItem.height
|
||||
clip: true
|
||||
interactive: false
|
||||
model: ProtocolsModel
|
||||
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
|
||||
}
|
||||
}
|
||||
|
||||
activeFocusOnTab: true
|
||||
focus: true
|
||||
model: ProtocolsModel
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: delegateContent.implicitHeight
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias focusItem: button
|
||||
LabelWithButtonType {
|
||||
id: button
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateContent
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
anchors.fill: parent
|
||||
text: qsTr("Show connection options")
|
||||
|
||||
LabelWithButtonType {
|
||||
id: button
|
||||
clickedFunction: function() {
|
||||
configContentDrawer.openTriggered()
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
MouseArea {
|
||||
anchors.fill: button
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
text: qsTr("Show connection options")
|
||||
DividerType {}
|
||||
|
||||
clickedFunction: function() {
|
||||
configContentDrawer.openTriggered()
|
||||
}
|
||||
DrawerType2 {
|
||||
id: configContentDrawer
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: button
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: false
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: configContentDrawer.expandedHeight
|
||||
|
||||
BackButtonType {
|
||||
id: drawerBackButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
|
||||
backButtonFunction: function() {
|
||||
configContentDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: drawerListView
|
||||
|
||||
anchors.top: drawerBackButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Connection options %1").arg(protocolName)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
DrawerType2 {
|
||||
id: configContentDrawer
|
||||
delegate: ColumnLayout {
|
||||
width: drawerListView.width
|
||||
|
||||
expandedHeight: root.height * 0.9
|
||||
TextArea {
|
||||
id: configText
|
||||
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
expandedStateContent: Item {
|
||||
implicitHeight: configContentDrawer.expandedHeight
|
||||
padding: 0
|
||||
height: 24
|
||||
|
||||
BackButtonType {
|
||||
id: backButton1
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
backButtonFunction: function() {
|
||||
configContentDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
text: rawConfig
|
||||
|
||||
FlickableType {
|
||||
anchors.top: backButton1.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
ColumnLayout {
|
||||
id: configContent
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
Header2Type {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
headerText: qsTr("Connection options %1").arg(protocolName)
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: configText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
padding: 0
|
||||
leftPadding: 0
|
||||
height: 24
|
||||
|
||||
color: AmneziaStyle.color.paleGray
|
||||
selectionColor: AmneziaStyle.color.richBrown
|
||||
selectedTextColor: AmneziaStyle.color.paleGray
|
||||
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.Medium
|
||||
font.family: "PT Root UI VF"
|
||||
|
||||
text: rawConfig
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: AmneziaStyle.color.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
@@ -198,11 +188,7 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
@@ -16,179 +16,158 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: ShadowSocksConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
spacing: 0
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
}
|
||||
|
||||
model: ShadowSocksConfigModel
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
enabled: listView.enabled
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
|
||||
spacing: 0
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("Shadowsocks settings")
|
||||
}
|
||||
enabled: listView.enabled
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
drawerParent: root
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
listView: ListViewWithRadioButtonType {
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
id: cipherListView
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: cipherDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
descriptionText: qsTr("Cipher")
|
||||
headerText: qsTr("Cipher")
|
||||
|
||||
drawerParent: root
|
||||
|
||||
listView: ListViewWithRadioButtonType {
|
||||
|
||||
id: cipherListView
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
model: ListModel {
|
||||
ListElement { name : "chacha20-ietf-poly1305" }
|
||||
ListElement { name : "xchacha20-ietf-poly1305" }
|
||||
ListElement { name : "aes-256-gcm" }
|
||||
ListElement { name : "aes-192-gcm" }
|
||||
ListElement { name : "aes-128-gcm" }
|
||||
}
|
||||
|
||||
clickedFunction: function() {
|
||||
cipherDropDown.text = selectedText
|
||||
cipher = cipherDropDown.text
|
||||
cipherDropDown.closeTriggered()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
cipherDropDown.text = cipher
|
||||
|
||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(ShadowSocksConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,160 +16,124 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
Item {
|
||||
id: focusItem
|
||||
onFocusChanged: {
|
||||
if (activeFocus) {
|
||||
fl.ensureVisible(focusItem)
|
||||
}
|
||||
}
|
||||
KeyNavigation.tab: backButton
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
model: WireGuardConfigModel
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
spacing: 0
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
model: WireGuardConfigModel
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
property alias mtuTextField: mtuTextField
|
||||
property bool isSaveButtonEnabled: mtuTextField.errorText === ""
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: mtuTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
headerText: qsTr("MTU")
|
||||
textField.text: clientMtu
|
||||
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
KeyNavigation.tab: saveButton
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== clientMtu) {
|
||||
clientMtu = textField.text
|
||||
}
|
||||
}
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Server settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: false
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
anchors.right: root.right
|
||||
anchors.left: root.left
|
||||
anchors.bottom: root.bottom
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
anchors.topMargin: 24
|
||||
anchors.bottomMargin: 24
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
enabled: listview.currentItem.isSaveButtonEnabled
|
||||
enabled: listView.currentItem.isSaveButtonEnabled
|
||||
|
||||
text: qsTr("Save")
|
||||
text: qsTr("Save")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
clickedFunc: function() {
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("Only the settings for this device will be changed")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,153 +16,130 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: WireGuardConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
spacing: 0
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
model: WireGuardConfigModel
|
||||
headerText: qsTr("WG settings")
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
TextFieldWithHeaderType {
|
||||
id: vpnAddressSubnetTextField
|
||||
|
||||
property alias focusItemId: vpnAddressSubnetTextField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
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: vpnAddressSubnetTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("VPN address subnet")
|
||||
textField.text: subnetAddress
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: this.clicked()
|
||||
Keys.onReturnPressed: this.clicked()
|
||||
}
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== subnetAddress) {
|
||||
subnetAddress = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === "" &&
|
||||
vpnAddressSubnetTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(WireGuardConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,163 +17,141 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
|
||||
Column {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
|
||||
model: XrayConfigModel
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
|
||||
property alias focusItemId: textFieldWithHeaderType.textField
|
||||
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: textFieldWithHeaderType
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
if (tmpText.startsWith("https://")) {
|
||||
tmpText = textField.text.substring(8)
|
||||
site = tmpText
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
enabled: delegateItem.isEnabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(XrayConfigModel.getConfig())
|
||||
//focusItem.forceActiveFocus()
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
model: XrayConfigModel
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
property alias focusItemId: textFieldWithHeaderType.textField
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
headerText: qsTr("XRay settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: textFieldWithHeaderType
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: listView.enabled
|
||||
|
||||
headerText: qsTr("Disguised as traffic from")
|
||||
textField.text: site
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== site) {
|
||||
var tmpText = textField.text
|
||||
tmpText = tmpText.toLocaleLowerCase()
|
||||
|
||||
if (tmpText.startsWith("https://")) {
|
||||
tmpText = textField.text.substring(8)
|
||||
site = tmpText
|
||||
} else {
|
||||
site = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: listView.enabled
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
checkEmptyText: true
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
enabled: portTextField.errorText === ""
|
||||
|
||||
text: qsTr("Save")
|
||||
|
||||
onClicked: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
var headerText = qsTr("Save settings?")
|
||||
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
|
||||
var yesButtonText = qsTr("Continue")
|
||||
var noButtonText = qsTr("Cancel")
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
|
||||
InstallController.updateContainer(XrayConfigModel.getConfig())
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
saveButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: saveButton.clicked()
|
||||
Keys.onReturnPressed: saveButton.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,50 +16,53 @@ import "../Components"
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
headerText: "AmneziaDNS"
|
||||
descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") +
|
||||
qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: removeButton
|
||||
|
||||
Layout.topMargin: 24
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
@@ -71,19 +74,14 @@ PageType {
|
||||
|
||||
var yesButtonFunction = function() {
|
||||
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
|
||||
&& SettingsController.isAmneziaDnsEnabled()) {
|
||||
&& SettingsController.isAmneziaDnsEnabled()) {
|
||||
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageDeinstalling)
|
||||
InstallController.removeProcessedContainer()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {
|
||||
if (!GC.isMobile()) {
|
||||
removeButton.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
var noButtonFunction = function() {}
|
||||
|
||||
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||
}
|
||||
|
||||
@@ -24,258 +24,215 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
Column {
|
||||
id: content
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: SftpConfigModel
|
||||
|
||||
enabled: ServersModel.isProcessedServerHasWriteAccess()
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
spacing: 0
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
headerText: qsTr("SFTP settings")
|
||||
}
|
||||
|
||||
model: SftpConfigModel
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
listview.currentItem.listViewFocusItem.forceActiveFocus()
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: mountButton
|
||||
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
text: qsTr("Mount folder on device")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.mountSftpDrive(port, password, username)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
|
||||
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
|
||||
|
||||
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
|
||||
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
|
||||
if (Qt.platform.os === "windows") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "osx") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "linux") {
|
||||
return ""
|
||||
} else return ""
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: col.implicitHeight
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
property alias listViewFocusItem: hostLabel.rightButton
|
||||
BasicButtonType {
|
||||
id: detailedInstructionsButton
|
||||
|
||||
ColumnLayout {
|
||||
id: col
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 8
|
||||
implicitHeight: 32
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
spacing: 0
|
||||
text: qsTr("Detailed instructions")
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("SFTP settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: mountButton
|
||||
visible: !GC.isMobile()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
text: qsTr("Mount folder on device")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
InstallController.mountSftpDrive(port, password, username)
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
readonly property string windowsFirstLink: "<a href=\"https://github.com/billziss-gh/winfsp/releases/latest\" style=\"color: #FBB26A;\">WinFsp</a>"
|
||||
readonly property string windowsSecondLink: "<a href=\"https://github.com/billziss-gh/sshfs-win/releases\" style=\"color: #FBB26A;\">SSHFS-Win</a>"
|
||||
|
||||
readonly property string macosFirstLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">macFUSE</a>"
|
||||
readonly property string macosSecondLink: "<a href=\"https://osxfuse.github.io/\" style=\"color: #FBB26A;\">SSHFS</a>"
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps: <br>")
|
||||
if (Qt.platform.os === "windows") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + windowsFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + windowsSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "osx") {
|
||||
str += qsTr("<br>1. Install the latest version of ") + macosFirstLink + "\n"
|
||||
str += qsTr("<br>2. Install the latest version of ") + macosSecondLink + "\n"
|
||||
} else if (Qt.platform.os === "linux") {
|
||||
return ""
|
||||
} else return ""
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: detailedInstructionsButton
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 8
|
||||
implicitHeight: 32
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("Detailed instructions")
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
clickedFunc: function() {
|
||||
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||
}
|
||||
}
|
||||
}
|
||||
clickedFunc: function() {
|
||||
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,327 +25,290 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: listview.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ListView {
|
||||
id: listview
|
||||
model: Socks5ProxyConfigModel
|
||||
|
||||
width: parent.width
|
||||
height: listview.contentItem.height
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
clip: true
|
||||
interactive: false
|
||||
spacing: 0
|
||||
|
||||
model: Socks5ProxyConfigModel
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
listview.currentItem.focusItemId.forceActiveFocus()
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
implicitWidth: listview.width
|
||||
implicitHeight: content.implicitHeight
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
property alias focusItemId: hostLabel.rightButton
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: changeSettingsDrawer
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
property string tempPort: port
|
||||
property string tempUsername: username
|
||||
property string tempPassword: password
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 32
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
Connections {
|
||||
target: changeSettingsDrawer
|
||||
function onOpened() {
|
||||
tempPort = port
|
||||
tempUsername = username
|
||||
tempPassword = password
|
||||
}
|
||||
function onClosed() {
|
||||
port = tempPort
|
||||
username = tempUsername
|
||||
password = tempPassword
|
||||
portTextField.textField.text = port
|
||||
usernameTextField.textField.text = username
|
||||
passwordTextField.textField.text = password
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: hostLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.topMargin: 40
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
parentFlickable: fl
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
text: qsTr("Host")
|
||||
descriptionText: ServersModel.getProcessedServerData("hostName")
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: portLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: usernameTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Port")
|
||||
descriptionText: port
|
||||
headerText: qsTr("Username")
|
||||
textField.placeholderText: "username"
|
||||
textField.text: username
|
||||
textField.maximumLength: 32
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== username) {
|
||||
username = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: usernameLabel
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
property bool hidePassword: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("User name")
|
||||
descriptionText: username
|
||||
headerText: qsTr("Password")
|
||||
textField.placeholderText: "password"
|
||||
textField.text: password
|
||||
textField.maximumLength: 32
|
||||
|
||||
descriptionOnTop: true
|
||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
||||
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
||||
: ""
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
clickedFunc: function() {
|
||||
hidePassword = !hidePassword
|
||||
}
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: passwordLabel
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password")
|
||||
descriptionText: password
|
||||
|
||||
descriptionOnTop: true
|
||||
|
||||
parentFlickable: fl
|
||||
|
||||
rightImageSource: "qrc:/images/controls/copy.svg"
|
||||
rightImageColor: AmneziaStyle.color.paleGray
|
||||
|
||||
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawerType2 {
|
||||
id: changeSettingsDrawer
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.9
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
property string tempPort: port
|
||||
property string tempUsername: username
|
||||
property string tempPassword: password
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 32
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 0
|
||||
|
||||
Connections {
|
||||
target: changeSettingsDrawer
|
||||
function onOpened() {
|
||||
tempPort = port
|
||||
tempUsername = username
|
||||
tempPassword = password
|
||||
}
|
||||
function onClosed() {
|
||||
port = tempPort
|
||||
username = tempUsername
|
||||
password = tempPassword
|
||||
portTextField.textField.text = port
|
||||
usernameTextField.textField.text = username
|
||||
passwordTextField.textField.text = password
|
||||
}
|
||||
}
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("SOCKS5 settings")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: portTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Port")
|
||||
textField.text: port
|
||||
textField.maximumLength: 5
|
||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== port) {
|
||||
port = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: usernameTextField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Username")
|
||||
textField.placeholderText: "username"
|
||||
textField.text: username
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.onEditingFinished: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== username) {
|
||||
username = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordTextField
|
||||
|
||||
property bool hidePassword: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
parentFlickable: fl
|
||||
|
||||
headerText: qsTr("Password")
|
||||
textField.placeholderText: "password"
|
||||
textField.text: password
|
||||
textField.maximumLength: 32
|
||||
|
||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
||||
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
||||
: ""
|
||||
|
||||
clickedFunc: function() {
|
||||
hidePassword = !hidePassword
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== password) {
|
||||
password = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
|
||||
if (!portTextField.textField.acceptableInput) {
|
||||
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
|
||||
passwordTextField.errorText = qsTr("Password cannot be empty")
|
||||
return
|
||||
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
|
||||
usernameTextField.errorText = qsTr("Username cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
|
||||
tempPort = portTextField.textField.text
|
||||
tempUsername = usernameTextField.textField.text
|
||||
tempPassword = passwordTextField.textField.text
|
||||
changeSettingsDrawer.closeTriggered()
|
||||
}
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
if (textField.text !== password) {
|
||||
password = textField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changeSettingsButton
|
||||
id: saveButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
forceActiveFocus()
|
||||
changeSettingsDrawer.openTriggered()
|
||||
if (!portTextField.textField.acceptableInput) {
|
||||
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
|
||||
return
|
||||
}
|
||||
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
|
||||
passwordTextField.errorText = qsTr("Password cannot be empty")
|
||||
return
|
||||
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
|
||||
usernameTextField.errorText = qsTr("Username cannot be empty")
|
||||
return
|
||||
}
|
||||
|
||||
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
|
||||
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
|
||||
tempPort = portTextField.textField.text
|
||||
tempUsername = usernameTextField.textField.text
|
||||
tempPassword = passwordTextField.textField.text
|
||||
changeSettingsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changeSettingsButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 24
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change connection settings")
|
||||
|
||||
clickedFunc: function() {
|
||||
changeSettingsDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,34 +25,31 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: backButtonLayout
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 20
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: backButtonLayout.bottom
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.implicitHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
@@ -61,11 +58,19 @@ PageType {
|
||||
|
||||
headerText: qsTr("Tor website settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
|
||||
delegate: ColumnLayout { // TODO(CyAn84): add DelegateChooser after migrate to 6.9
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: websiteName
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Website address")
|
||||
descriptionText: {
|
||||
@@ -83,15 +88,16 @@ PageType {
|
||||
clickedFunction: function() {
|
||||
GC.copyToClipBoard(descriptionText)
|
||||
PageController.showNotificationMessage(qsTr("Copied"))
|
||||
if (!GC.isMobile()) {
|
||||
this.rightButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
|
||||
@@ -1,156 +1,163 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
contentHeight: content.height
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Settings")
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: account
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
text: qsTr("Servers")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/server.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsServersList)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: connection
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Connection")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/radio.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsConnection)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: application
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Application")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/app.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsApplication)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: backup
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Backup")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/save.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsBackup)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: about
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("About AmneziaVPN")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/amnezia.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAbout)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: devConsole
|
||||
visible: SettingsController.isDevModeEnabled
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Dev console")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: "qrc:/images/controls/bug.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.goToPage(PageEnum.PageDevMenu)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: SettingsController.isDevModeEnabled
|
||||
}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: close
|
||||
visible: GC.isDesktop()
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: about.height
|
||||
|
||||
text: qsTr("Close application")
|
||||
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
||||
isLeftImageHoverEnabled: false
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.closeApplication()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: GC.isDesktop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
headerText: qsTr("Settings")
|
||||
}
|
||||
}
|
||||
|
||||
model: settingsEntries
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: isVisible
|
||||
|
||||
text: title
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
leftImageSource: leftImagePath
|
||||
|
||||
clickedFunction: clickedHandler
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithButtonType {
|
||||
id: close
|
||||
|
||||
visible: GC.isDesktop()
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Close application")
|
||||
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
||||
isLeftImageHoverEnabled: false
|
||||
|
||||
clickedFunction: function() {
|
||||
PageController.closeApplication()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: GC.isDesktop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> settingsEntries: [
|
||||
servers,
|
||||
connection,
|
||||
application,
|
||||
backup,
|
||||
about,
|
||||
devConsole
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: servers
|
||||
|
||||
property string title: qsTr("Servers")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsServersList)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: connection
|
||||
|
||||
property string title: qsTr("Connection")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/radio.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsConnection)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: application
|
||||
|
||||
property string title: qsTr("Application")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/app.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsApplication)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: backup
|
||||
|
||||
property string title: qsTr("Backup")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/save.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsBackup)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: about
|
||||
|
||||
property string title: qsTr("About AmneziaVPN")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg"
|
||||
property bool isVisible: true
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageSettingsAbout)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: devConsole
|
||||
|
||||
property string title: qsTr("Dev console")
|
||||
readonly property string leftImagePath: "qrc:/images/controls/bug.svg"
|
||||
property bool isVisible: SettingsController.isDevModeEnabled
|
||||
readonly property var clickedHandler: function() {
|
||||
PageController.goToPage(PageEnum.PageDevMenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||