replace FlickableType and ListView with

`ListViewType`
This commit is contained in:
Cyril Anisimov 2025-04-13 20:05:22 +02:00
parent a2d30efaab
commit 7d187bf881
58 changed files with 6221 additions and 6762 deletions

View file

@ -1,309 +1,311 @@
#include "listViewFocusController.h" #include "listViewFocusController.h"
#include "utils/qmlUtils.h" #include "utils/qmlUtils.h"
#include <QQuickWindow> #include <QQuickWindow>
ListViewFocusController::ListViewFocusController(QQuickItem *listView, QObject *parent) ListViewFocusController::ListViewFocusController(QQuickItem *listView, QObject *parent)
: QObject { parent }, : QObject { parent },
m_listView { listView }, m_listView { listView },
m_focusChain {}, m_focusChain {},
m_currentSection { Section::Default }, m_currentSection { Section::Default },
m_header { nullptr }, m_header { nullptr },
m_footer { nullptr }, m_footer { nullptr },
m_focusedItem { nullptr }, m_focusedItem { nullptr },
m_focusedItemIndex { -1 }, m_focusedItemIndex { -1 },
m_delegateIndex { 0 }, m_delegateIndex { 0 },
m_isReturnNeeded { false }, m_isReturnNeeded { false },
m_currentSectionString { "Default", "Header", "Delegate", "Footer" } m_currentSectionString { "Default", "Header", "Delegate", "Footer" }
{ {
QVariant headerItemProperty = m_listView->property("headerItem"); QVariant headerItemProperty = m_listView->property("headerItem");
m_header = headerItemProperty.canConvert<QQuickItem *>() ? headerItemProperty.value<QQuickItem *>() : nullptr; m_header = headerItemProperty.canConvert<QQuickItem *>() ? headerItemProperty.value<QQuickItem *>() : nullptr;
QVariant footerItemProperty = m_listView->property("footerItem"); QVariant footerItemProperty = m_listView->property("footerItem");
m_footer = footerItemProperty.canConvert<QQuickItem *>() ? footerItemProperty.value<QQuickItem *>() : nullptr; m_footer = footerItemProperty.canConvert<QQuickItem *>() ? footerItemProperty.value<QQuickItem *>() : nullptr;
} }
ListViewFocusController::~ListViewFocusController() ListViewFocusController::~ListViewFocusController()
{ {
} }
void ListViewFocusController::viewAtCurrentIndex() const void ListViewFocusController::viewAtCurrentIndex() const
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: [[fallthrough]]; case Section::Default: [[fallthrough]];
case Section::Header: { case Section::Header: {
QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning"); QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning");
break; break;
} }
case Section::Delegate: { case Section::Delegate: {
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
Q_ARG(int, 2)); // PositionMode (0 = Visible) Q_ARG(int, 2)); // PositionMode (0 = Visible)
break; break;
} }
case Section::Footer: { case Section::Footer: {
QMetaObject::invokeMethod(m_listView, "positionViewAtEnd"); QMetaObject::invokeMethod(m_listView, "positionViewAtEnd");
break; break;
} }
} }
} }
int ListViewFocusController::size() const int ListViewFocusController::size() const
{ {
return m_listView->property("count").toInt(); return m_listView->property("count").toInt();
} }
int ListViewFocusController::currentIndex() const int ListViewFocusController::currentIndex() const
{ {
return m_delegateIndex; return m_delegateIndex;
} }
void ListViewFocusController::setDelegateIndex(int index) void ListViewFocusController::setDelegateIndex(int index)
{ {
m_delegateIndex = index; m_delegateIndex = index;
m_listView->setProperty("currentIndex", index); m_listView->setProperty("currentIndex", index);
} }
void ListViewFocusController::nextDelegate() void ListViewFocusController::nextDelegate()
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
if (hasHeader()) { if (hasHeader()) {
m_currentSection = Section::Header; m_currentSection = Section::Header;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Header: { case Section::Header: {
if (size() > 0) { if (size() > 0) {
m_currentSection = Section::Delegate; m_currentSection = Section::Delegate;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Delegate: case Section::Delegate:
if (m_delegateIndex < (size() - 1)) { if (m_delegateIndex < (size() - 1)) {
setDelegateIndex(m_delegateIndex + 1); setDelegateIndex(m_delegateIndex + 1);
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} else if (hasFooter()) { } else if (hasFooter()) {
m_currentSection = Section::Footer; m_currentSection = Section::Footer;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
[[fallthrough]]; [[fallthrough]];
case Section::Footer: { case Section::Footer: {
m_isReturnNeeded = true; m_isReturnNeeded = true;
m_currentSection = Section::Default; m_currentSection = Section::Default;
viewAtCurrentIndex(); viewAtCurrentIndex();
break; break;
} }
default: { default: {
qCritical() << "Current section is invalid!"; qCritical() << "Current section is invalid!";
break; break;
} }
} }
} }
void ListViewFocusController::previousDelegate() void ListViewFocusController::previousDelegate()
{ {
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
if (hasFooter()) { if (hasFooter()) {
m_currentSection = Section::Footer; m_currentSection = Section::Footer;
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Footer: { case Section::Footer: {
if (size() > 0) { if (size() > 0) {
m_currentSection = Section::Delegate; m_currentSection = Section::Delegate;
setDelegateIndex(size() - 1); setDelegateIndex(size() - 1);
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Delegate: { case Section::Delegate: {
if (m_delegateIndex > 0) { if (m_delegateIndex > 0) {
setDelegateIndex(m_delegateIndex - 1); setDelegateIndex(m_delegateIndex - 1);
break; break;
} else if (hasHeader()) { } else if (hasHeader()) {
m_currentSection = Section::Header; m_currentSection = Section::Header;
break; break;
} }
[[fallthrough]]; [[fallthrough]];
} }
case Section::Header: { case Section::Header: {
m_isReturnNeeded = true; m_isReturnNeeded = true;
m_currentSection = Section::Default; m_currentSection = Section::Default;
break; break;
} }
default: { default: {
qCritical() << "Current section is invalid!"; qCritical() << "Current section is invalid!";
break; break;
} }
} }
} }
void ListViewFocusController::decrementIndex() void ListViewFocusController::decrementIndex()
{ {
m_delegateIndex--; m_delegateIndex--;
} }
QQuickItem *ListViewFocusController::itemAtIndex(const int index) const QQuickItem *ListViewFocusController::itemAtIndex(const int index) const
{ {
QQuickItem *item { nullptr }; QQuickItem *item { nullptr };
QMetaObject::invokeMethod(m_listView, "itemAtIndex", Q_RETURN_ARG(QQuickItem *, item), Q_ARG(int, index)); QMetaObject::invokeMethod(m_listView, "itemAtIndex", Q_RETURN_ARG(QQuickItem *, item), Q_ARG(int, index));
return item; return item;
} }
QQuickItem *ListViewFocusController::currentDelegate() const QQuickItem *ListViewFocusController::currentDelegate() const
{ {
QQuickItem *result { nullptr }; QQuickItem *result { nullptr };
switch (m_currentSection) { switch (m_currentSection) {
case Section::Default: { case Section::Default: {
qWarning() << "No elements..."; qWarning() << "No elements...";
break; break;
} }
case Section::Header: { case Section::Header: {
result = m_header; result = m_header;
break; break;
} }
case Section::Delegate: { case Section::Delegate: {
result = itemAtIndex(m_delegateIndex); result = itemAtIndex(m_delegateIndex);
break; break;
} }
case Section::Footer: { case Section::Footer: {
result = m_footer; result = m_footer;
break; break;
} }
} }
return result; return result;
} }
QQuickItem *ListViewFocusController::focusedItem() const QQuickItem *ListViewFocusController::focusedItem() const
{ {
return m_focusedItem; return m_focusedItem;
} }
void ListViewFocusController::focusNextItem() void ListViewFocusController::focusNextItem()
{ {
if (m_isReturnNeeded) { if (m_isReturnNeeded) {
return; return;
} }
reloadFocusChain(); reloadFocusChain();
if (m_focusChain.empty()) { if (m_focusChain.empty()) {
qWarning() << "No elements found in the delegate. Going to next delegate..."; qWarning() << "No elements found in the delegate. Going to next delegate...";
nextDelegate(); nextDelegate();
focusNextItem(); focusNextItem();
return; return;
} }
m_focusedItemIndex++; m_focusedItemIndex++;
m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex)); m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex));
m_focusedItem->forceActiveFocus(Qt::TabFocusReason); m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
} qDebug() << "Next focus is set to item: " << m_focusedItem;
}
void ListViewFocusController::focusPreviousItem()
{ void ListViewFocusController::focusPreviousItem()
if (m_isReturnNeeded) { {
return; if (m_isReturnNeeded) {
} return;
}
if (m_focusChain.empty()) {
qInfo() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; if (m_focusChain.empty()) {
reloadFocusChain(); qInfo() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements...";
} reloadFocusChain();
if (m_focusChain.empty()) { }
qWarning() << "No elements found in the delegate. Going to next delegate..."; if (m_focusChain.empty()) {
previousDelegate(); qWarning() << "No elements found in the delegate. Going to next delegate...";
focusPreviousItem(); previousDelegate();
return; focusPreviousItem();
} return;
if (m_focusedItemIndex == -1) { }
m_focusedItemIndex = m_focusChain.size(); if (m_focusedItemIndex == -1) {
} m_focusedItemIndex = m_focusChain.size();
m_focusedItemIndex--; }
m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex)); m_focusedItemIndex--;
m_focusedItem->forceActiveFocus(Qt::TabFocusReason); m_focusedItem = qobject_cast<QQuickItem *>(m_focusChain.at(m_focusedItemIndex));
} m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
}
void ListViewFocusController::resetFocusChain()
{ void ListViewFocusController::resetFocusChain()
m_focusChain.clear(); {
m_focusedItem = nullptr; m_focusChain.clear();
m_focusedItemIndex = -1; m_focusedItem = nullptr;
} m_focusedItemIndex = -1;
qDebug() << "Focus chain was resetted";
void ListViewFocusController::reloadFocusChain() }
{
m_focusChain = FocusControl::getItemsChain(currentDelegate()); void ListViewFocusController::reloadFocusChain()
} {
m_focusChain = FocusControl::getItemsChain(currentDelegate());
bool ListViewFocusController::isFirstFocusItemInDelegate() const }
{
return m_focusedItem && (m_focusedItem == m_focusChain.first()); bool ListViewFocusController::isFirstFocusItemInDelegate() const
} {
return m_focusedItem && (m_focusedItem == m_focusChain.first());
bool ListViewFocusController::isLastFocusItemInDelegate() const }
{
return m_focusedItem && (m_focusedItem == m_focusChain.last()); bool ListViewFocusController::isLastFocusItemInDelegate() const
} {
return m_focusedItem && (m_focusedItem == m_focusChain.last());
bool ListViewFocusController::hasHeader() const }
{
return m_header && !FocusControl::getItemsChain(m_header).isEmpty(); bool ListViewFocusController::hasHeader() const
} {
return m_header && !FocusControl::getItemsChain(m_header).isEmpty();
bool ListViewFocusController::hasFooter() const }
{
return m_footer && !FocusControl::getItemsChain(m_footer).isEmpty(); bool ListViewFocusController::hasFooter() const
} {
return m_footer && !FocusControl::getItemsChain(m_footer).isEmpty();
bool ListViewFocusController::isFirstFocusItemInListView() const }
{
switch (m_currentSection) { bool ListViewFocusController::isFirstFocusItemInListView() const
case Section::Footer: { {
return isFirstFocusItemInDelegate() && !hasHeader() && (size() == 0); switch (m_currentSection) {
} case Section::Footer: {
case Section::Delegate: { return isFirstFocusItemInDelegate() && !hasHeader() && (size() == 0);
return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader(); }
} case Section::Delegate: {
case Section::Header: { return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader();
isFirstFocusItemInDelegate(); }
} case Section::Header: {
case Section::Default: { isFirstFocusItemInDelegate();
return true; }
} case Section::Default: {
default: qWarning() << "Wrong section"; return true; return true;
} }
} default: qWarning() << "Wrong section"; return true;
}
bool ListViewFocusController::isLastFocusItemInListView() const }
{
switch (m_currentSection) { bool ListViewFocusController::isLastFocusItemInListView() const
case Section::Default: { {
return !hasHeader() && (size() == 0) && !hasFooter(); switch (m_currentSection) {
} case Section::Default: {
case Section::Header: { return !hasHeader() && (size() == 0) && !hasFooter();
return isLastFocusItemInDelegate() && (size() == 0) && !hasFooter(); }
} case Section::Header: {
case Section::Delegate: { return isLastFocusItemInDelegate() && (size() == 0) && !hasFooter();
return isLastFocusItemInDelegate() && (m_delegateIndex == size() - 1) && !hasFooter(); }
} case Section::Delegate: {
case Section::Footer: { return isLastFocusItemInDelegate() && (m_delegateIndex == size() - 1) && !hasFooter();
return isLastFocusItemInDelegate(); }
} case Section::Footer: {
default: qWarning() << "Wrong section"; return true; return isLastFocusItemInDelegate();
} }
} default: qWarning() << "Wrong section"; return true;
}
bool ListViewFocusController::isReturnNeeded() const }
{
return m_isReturnNeeded; bool ListViewFocusController::isReturnNeeded() const
} {
return m_isReturnNeeded;
}

View file

@ -10,8 +10,7 @@ import ProtocolEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListViewType {
ListView {
id: menuContent id: menuContent
property var rootWidth property var rootWidth
@ -21,13 +20,6 @@ ListView {
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
clip: true
snapMode: ListView.SnapToItem
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
ButtonGroup { ButtonGroup {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }

View file

@ -1,97 +1,97 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
DrawerType2 { DrawerType2 {
id: root id: root
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
expandedStateContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
headerText: qsTr("Split tunneling") headerText: qsTr("Split tunneling")
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others") descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
} }
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingSwitch id: splitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
text: qsTr("Split tunneling on the server") text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server") descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling visible: ServersModel.isDefaultServerDefaultContainerHasSplitTunneling
} }
LabelWithButtonType { LabelWithButtonType {
id: siteBasedSplitTunnelingSwitch id: siteBasedSplitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
} }
LabelWithButtonType { LabelWithButtonType {
id: appSplitTunnelingSwitch id: appSplitTunnelingSwitch
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("App-based split tunneling") text: qsTr("App-based split tunneling")
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.closeTriggered() root.closeTriggered()
} }
} }
DividerType { DividerType {
visible: isAppSplitTinnelingEnabled visible: isAppSplitTinnelingEnabled
} }
} }
} }

View file

@ -57,7 +57,7 @@ DrawerType2 {
headerText: qsTr("Choose application") headerText: qsTr("Choose application")
} }
ListView { ListViewType {
id: listView id: listView
Layout.fillWidth: true Layout.fillWidth: true
@ -66,11 +66,6 @@ DrawerType2 {
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
clip: true
interactive: true
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyInstalledAppsModel id: proxyInstalledAppsModel
sourceModel: installedAppsModel sourceModel: installedAppsModel
@ -81,44 +76,37 @@ DrawerType2 {
} }
} }
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }
delegate: Item { delegate: ColumnLayout {
implicitWidth: root.width id: delegateContent
implicitHeight: delegateContent.implicitHeight
ColumnLayout { width: listView.width
id: delegateContent
anchors.fill: parent RowLayout {
CheckBoxType {
Layout.fillWidth: true
RowLayout { text: appName
CheckBoxType { checked: isAppSelected
Layout.fillWidth: true onCheckedChanged: {
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
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
} }
} }
DividerType {} Image {
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
}
} }
DividerType {}
} }
} }
} }

View file

@ -49,7 +49,7 @@ DrawerType2 {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
@ -57,14 +57,8 @@ DrawerType2 {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
property bool isFocusable: true
property int selectedIndex: LanguageModel.currentLanguageIndex property int selectedIndex: LanguageModel.currentLanguageIndex
clip: true
reuseItems: true
ScrollBar.vertical: ScrollBarType {}
model: LanguageModel model: LanguageModel
ButtonGroup { ButtonGroup {

View file

@ -15,7 +15,7 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
ListView { ListViewType {
id: root id: root
property int selectedIndex: ServersModel.defaultIndex property int selectedIndex: ServersModel.defaultIndex
@ -28,10 +28,6 @@ ListView {
model: ServersModel model: ServersModel
ScrollBar.vertical: ScrollBarType {}
property bool isFocusable: true
Connections { Connections {
target: ServersModel target: ServersModel
function onDefaultServerIndexChanged(serverIndex) { function onDefaultServerIndexChanged(serverIndex) {
@ -39,9 +35,6 @@ ListView {
} }
} }
clip: true
reuseItems: true
delegate: Item { delegate: Item {
id: menuContentDelegate id: menuContentDelegate
objectName: "menuContentDelegate" objectName: "menuContentDelegate"

View file

@ -13,78 +13,64 @@ import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
ListView { ListViewType {
id: root id: listView
width: parent.width anchors.fill: parent
height: root.contentItem.height
clip: true delegate: ColumnLayout {
reuseItems: true width: listView.width
property bool isFocusable: false LabelWithButtonType {
Layout.fillWidth: true
delegate: Item { text: name
implicitWidth: root.width descriptionText: description
implicitHeight: delegateContent.implicitHeight rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
ColumnLayout { clickedFunction: function() {
id: delegateContent if (isInstalled) {
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
anchors.fill: parent if (serviceType !== ProtocolEnum.Other) {
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
LabelWithButtonType {
id: containerRadioButton
implicitWidth: parent.width
text: name
descriptionText: description
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
clickedFunction: function() {
if (isInstalled) {
var containerIndex = root.model.mapToSource(index)
ContainersModel.setProcessedContainerIndex(containerIndex)
if (serviceType !== ProtocolEnum.Other) {
if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) {
ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolRaw)
return
}
}
switch (containerIndex) {
case ContainerEnum.Ipsec: {
ProtocolsModel.updateModel(config) ProtocolsModel.updateModel(config)
PageController.goToPage(PageEnum.PageProtocolRaw) PageController.goToPage(PageEnum.PageProtocolRaw)
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 { switch (containerIndex) {
anchors.fill: parent case ContainerEnum.Ipsec: {
cursorShape: Qt.PointingHandCursor ProtocolsModel.updateModel(config)
enabled: false 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 {}
} }
} }

View file

@ -53,7 +53,7 @@ DrawerType2 {
headerText: root.headerText headerText: root.headerText
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
@ -61,14 +61,7 @@ DrawerType2 {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
property bool isFocusable: true model: 1 // fake model to force the ListView to be created without a model
ScrollBar.vertical: ScrollBarType {}
model: 1
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -214,24 +207,25 @@ DrawerType2 {
backButtonFunction: function() { configContentDrawer.closeTriggered() } backButtonFunction: function() { configContentDrawer.closeTriggered() }
} }
FlickableType { ListViewType {
id: configListView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: configContent.implicitHeight + configContent.anchors.topMargin + configContent.anchors.bottomMargin
ColumnLayout { model: 1 // fake model to force the ListView to be created without a model
id: configContent
anchors.fill: parent header: ColumnLayout {
anchors.rightMargin: 16 width: configListView.width
anchors.leftMargin: 16
Header2Type { Header2Type {
id: configContentHeader id: configContentHeader
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: root.configContentHeaderText headerText: root.configContentHeaderText
} }

View file

@ -1,220 +1,210 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string hoveredColor: AmneziaStyle.color.lightGray property string hoveredColor: AmneziaStyle.color.lightGray
property string defaultColor: AmneziaStyle.color.paleGray property string defaultColor: AmneziaStyle.color.paleGray
property string disabledColor: AmneziaStyle.color.charcoalGray property string disabledColor: AmneziaStyle.color.charcoalGray
property string pressedColor: AmneziaStyle.color.mutedGray property string pressedColor: AmneziaStyle.color.mutedGray
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string borderColor: AmneziaStyle.color.paleGray property string borderColor: AmneziaStyle.color.paleGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderWidth: 0 property int borderWidth: 0
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string leftImageSource property string leftImageSource
property string rightImageSource property string rightImageSource
property string leftImageColor: textColor property string leftImageColor: textColor
property bool changeLeftImageSize: true property bool changeLeftImageSize: true
property bool squareLeftSide: false property bool squareLeftSide: false
property FlickableType parentFlickable property var clickedFunc
property var clickedFunc property alias buttonTextLabel: buttonText
property alias buttonTextLabel: buttonText property bool isFocusable: true
property bool isFocusable: true Keys.onTabPressed: {
FocusController.nextKeyTabItem()
Keys.onTabPressed: { }
FocusController.nextKeyTabItem()
} Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
Keys.onBacktabPressed: { }
FocusController.previousKeyTabItem()
} Keys.onUpPressed: {
FocusController.nextKeyUpItem()
Keys.onUpPressed: { }
FocusController.nextKeyUpItem()
} Keys.onDownPressed: {
FocusController.nextKeyDownItem()
Keys.onDownPressed: { }
FocusController.nextKeyDownItem()
} Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
Keys.onLeftPressed: { }
FocusController.nextKeyLeftItem()
} Keys.onRightPressed: {
FocusController.nextKeyRightItem()
Keys.onRightPressed: { }
FocusController.nextKeyRightItem()
} implicitHeight: 56
implicitHeight: 56 hoverEnabled: true
hoverEnabled: true background: Rectangle {
id: focusBorder
onFocusChanged: {
if (root.activeFocus) { color: AmneziaStyle.color.transparent
if (root.parentFlickable) { border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
root.parentFlickable.ensureVisible(this) border.width: root.activeFocus ? root.borderFocusedWidth : 0
}
} anchors.fill: parent
}
radius: 16
background: Rectangle {
id: focusBorder Rectangle {
id: background
color: AmneziaStyle.color.transparent
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent anchors.fill: focusBorder
border.width: root.activeFocus ? root.borderFocusedWidth : 0 anchors.margins: root.activeFocus ? 2 : 0
anchors.fill: parent radius: root.activeFocus ? 14 : 16
color: {
radius: 16 if (root.enabled) {
if (root.pressed) {
Rectangle { return pressedColor
id: background }
return root.hovered ? hoveredColor : defaultColor
anchors.fill: focusBorder } else {
anchors.margins: root.activeFocus ? 2 : 0 return disabledColor
}
radius: root.activeFocus ? 14 : 16 }
color: { border.color: borderColor
if (root.enabled) { border.width: borderWidth
if (root.pressed) {
return pressedColor Behavior on color {
} PropertyAnimation { duration: 200 }
return root.hovered ? hoveredColor : defaultColor }
} else {
return disabledColor Rectangle {
} visible: root.squareLeftSide
}
border.color: borderColor z: 1
border.width: borderWidth
width: parent.radius
Behavior on color { height: parent.radius
PropertyAnimation { duration: 200 } anchors.top: parent.top
} anchors.bottom: parent.bottom
anchors.left: parent.left
Rectangle { color: {
visible: root.squareLeftSide if (root.enabled) {
if (root.pressed) {
z: 1 return pressedColor
}
width: parent.radius return root.hovered ? hoveredColor : defaultColor
height: parent.radius } else {
anchors.top: parent.top return disabledColor
anchors.bottom: parent.bottom }
anchors.left: parent.left }
color: {
if (root.enabled) { Behavior on color {
if (root.pressed) { PropertyAnimation { duration: 200 }
return pressedColor }
} }
return root.hovered ? hoveredColor : defaultColor }
} else { }
return disabledColor
} MouseArea {
} anchors.fill: focusBorder
enabled: false
Behavior on color { cursorShape: Qt.PointingHandCursor
PropertyAnimation { duration: 200 } }
}
} contentItem: Item {
} anchors.fill: focusBorder
}
implicitWidth: content.implicitWidth
MouseArea { implicitHeight: content.implicitHeight
anchors.fill: focusBorder
enabled: false RowLayout {
cursorShape: Qt.PointingHandCursor id: content
} anchors.centerIn: parent
contentItem: Item { Image {
anchors.fill: focusBorder id: leftImage
source: root.leftImageSource
implicitWidth: content.implicitWidth visible: root.leftImageSource === "" ? false : true
implicitHeight: content.implicitHeight
layer {
RowLayout { enabled: leftImageColor !== "" ? true : false
id: content effect: ColorOverlay {
anchors.centerIn: parent color: leftImageColor
}
Image { }
id: leftImage
source: root.leftImageSource Component.onCompleted: {
visible: root.leftImageSource === "" ? false : true if (root.changeLeftImageSize) {
leftImage.Layout.preferredHeight = 20
layer { leftImage.Layout.preferredWidth = 20
enabled: leftImageColor !== "" ? true : false }
effect: ColorOverlay { }
color: leftImageColor }
}
} ButtonTextType {
id: buttonText
Component.onCompleted: {
if (root.changeLeftImageSize) { color: root.textColor
leftImage.Layout.preferredHeight = 20 text: root.text
leftImage.Layout.preferredWidth = 20 visible: root.text === "" ? false : true
}
} horizontalAlignment: Text.AlignLeft
} verticalAlignment: Text.AlignVCenter
}
ButtonTextType {
id: buttonText Image {
Layout.preferredHeight: 20
color: root.textColor Layout.preferredWidth: 20
text: root.text
visible: root.text === "" ? false : true source: root.rightImageSource
visible: root.rightImageSource === "" ? false : true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter layer {
} enabled: true
effect: ColorOverlay {
Image { color: textColor
Layout.preferredHeight: 20 }
Layout.preferredWidth: 20 }
}
source: root.rightImageSource }
visible: root.rightImageSource === "" ? false : true }
layer { Keys.onEnterPressed: {
enabled: true if (root.clickedFunc && typeof root.clickedFunc === "function") {
effect: ColorOverlay { root.clickedFunc()
color: textColor }
} }
}
} Keys.onReturnPressed: {
} if (root.clickedFunc && typeof root.clickedFunc === "function") {
} root.clickedFunc()
}
Keys.onEnterPressed: { }
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc() onClicked: {
} 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()
}
}
}

View file

@ -1,203 +1,185 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Button { Button {
id: root id: root
property string headerText property string headerText
property string bodyText property string bodyText
property string footerText property string footerText
property string hoveredColor: AmneziaStyle.color.slateGray property string hoveredColor: AmneziaStyle.color.slateGray
property string defaultColor: AmneziaStyle.color.onyxBlack property string defaultColor: AmneziaStyle.color.onyxBlack
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string rightImageSource property string rightImageSource
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property string leftImageSource property string leftImageSource
property real textOpacity: 1.0 property real textOpacity: 1.0
property alias focusItem: rightImage property alias focusItem: rightImage
property FlickableType parentFlickable hoverEnabled: true
hoverEnabled: true background: Rectangle {
id: backgroundRect
background: Rectangle {
id: backgroundRect anchors.fill: parent
radius: 16
anchors.fill: parent
radius: 16 color: defaultColor
color: defaultColor Behavior on color {
PropertyAnimation { duration: 200 }
Behavior on color { }
PropertyAnimation { duration: 200 } }
}
} contentItem: Item {
anchors.left: parent.left
function ensureVisible(item) { anchors.right: parent.right
if (item.activeFocus) {
if (root.parentFlickable) { implicitHeight: content.implicitHeight
root.parentFlickable.ensureVisible(root)
} RowLayout {
} id: content
}
anchors.fill: parent
onFocusChanged: {
ensureVisible(root) Image {
} id: leftImage
source: leftImageSource
focusItem.onFocusChanged: {
root.ensureVisible(focusItem) visible: leftImageSource !== ""
}
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
contentItem: Item { Layout.topMargin: 24
anchors.left: parent.left Layout.bottomMargin: 24
anchors.right: parent.right Layout.leftMargin: 24
}
implicitHeight: content.implicitHeight
ColumnLayout {
RowLayout {
id: content ListItemTitleType {
text: root.headerText
anchors.fill: parent visible: text !== ""
Image { Layout.fillWidth: true
id: leftImage Layout.rightMargin: 16
source: leftImageSource Layout.leftMargin: 16
Layout.topMargin: 16
visible: leftImageSource !== "" Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
Layout.alignment: Qt.AlignLeft | Qt.AlignTop opacity: root.textOpacity
Layout.topMargin: 24 }
Layout.bottomMargin: 24
Layout.leftMargin: 24 CaptionTextType {
} text: root.bodyText
visible: text !== ""
ColumnLayout {
color: AmneziaStyle.color.mutedGray
ListItemTitleType { textFormat: Text.RichText
text: root.headerText
visible: text !== "" Layout.fillWidth: true
Layout.rightMargin: 16
Layout.fillWidth: true Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.bottomMargin: root.footerText !== "" ? 0 : 16
Layout.leftMargin: 16
Layout.topMargin: 16 opacity: root.textOpacity
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16 }
opacity: root.textOpacity ButtonTextType {
} text: root.footerText
visible: text !== ""
CaptionTextType {
text: root.bodyText color: AmneziaStyle.color.mutedGray
visible: text !== ""
Layout.fillWidth: true
color: AmneziaStyle.color.mutedGray Layout.rightMargin: 16
textFormat: Text.RichText Layout.leftMargin: 16
Layout.topMargin: 16
Layout.fillWidth: true Layout.bottomMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16 opacity: root.textOpacity
Layout.bottomMargin: root.footerText !== "" ? 0 : 16 }
}
opacity: root.textOpacity
} ImageButtonType {
id: rightImage
ButtonTextType {
text: root.footerText implicitWidth: 40
visible: text !== "" implicitHeight: 40
color: AmneziaStyle.color.mutedGray hoverEnabled: false
image: rightImageSource
Layout.fillWidth: true imageColor: rightImageColor
Layout.rightMargin: 16 visible: rightImageSource ? true : false
Layout.leftMargin: 16
Layout.topMargin: 16 Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.bottomMargin: 16 Layout.topMargin: 16
Layout.bottomMargin: 16
opacity: root.textOpacity Layout.rightMargin: 16
}
} Rectangle {
id: rightImageBackground
ImageButtonType {
id: rightImage anchors.fill: parent
radius: 12
implicitWidth: 40 color: "transparent"
implicitHeight: 40
Behavior on color {
hoverEnabled: false PropertyAnimation { duration: 200 }
image: rightImageSource }
imageColor: rightImageColor }
visible: rightImageSource ? true : false
onClicked: {
Layout.alignment: Qt.AlignRight | Qt.AlignTop root.clicked()
Layout.topMargin: 16 }
Layout.bottomMargin: 16 }
Layout.rightMargin: 16 }
}
Rectangle {
id: rightImageBackground MouseArea {
anchors.fill: parent
anchors.fill: parent
radius: 12 cursorShape: Qt.PointingHandCursor
color: "transparent" hoverEnabled: true
enabled: root.enabled
Behavior on color {
PropertyAnimation { duration: 200 } onEntered: {
} backgroundRect.color = root.hoveredColor
}
if (rightImageSource) {
onClicked: { rightImageBackground.color = rightImage.hoveredColor
root.clicked() }
} root.textOpacity = 0.8
} }
}
} onExited: {
backgroundRect.color = root.defaultColor
MouseArea {
anchors.fill: parent if (rightImageSource) {
rightImageBackground.color = rightImage.defaultColor
cursorShape: Qt.PointingHandCursor }
hoverEnabled: true root.textOpacity = 1
enabled: root.enabled }
onEntered: { onPressedChanged: {
backgroundRect.color = root.hoveredColor if (rightImageSource) {
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
if (rightImageSource) { }
rightImageBackground.color = rightImage.hoveredColor root.textOpacity = 0.7
} }
root.textOpacity = 0.8
} onClicked: {
root.clicked()
onExited: { }
backgroundRect.color = root.defaultColor }
}
if (rightImageSource) {
rightImageBackground.color = rightImage.defaultColor
}
root.textOpacity = 1
}
onPressedChanged: {
if (rightImageSource) {
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
}
root.textOpacity = 0.7
}
onClicked: {
root.clicked()
}
}
}

View file

@ -1,170 +1,161 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
CheckBox { CheckBox {
id: root id: root
property string descriptionText property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultBorderColor: AmneziaStyle.color.paleGray property string defaultBorderColor: AmneziaStyle.color.paleGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string checkedImageColor: AmneziaStyle.color.goldenApricot property string checkedImageColor: AmneziaStyle.color.goldenApricot
property string pressedImageColor: AmneziaStyle.color.burntOrange property string pressedImageColor: AmneziaStyle.color.burntOrange
property string defaultImageColor: AmneziaStyle.color.transparent property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
property string imageSource: "qrc:/images/controls/check.svg" property string imageSource: "qrc:/images/controls/check.svg"
property var parentFlickable hoverEnabled: enabled ? true : false
onFocusChanged: { focusPolicy: Qt.NoFocus
if (root.activeFocus) {
if (root.parentFlickable) { background: Rectangle {
root.parentFlickable.ensureVisible(root) color: AmneziaStyle.color.transparent
} border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
} border.width: 1
} radius: 16
}
hoverEnabled: enabled ? true : false
focusPolicy: Qt.NoFocus indicator: Rectangle {
id: background
background: Rectangle {
color: AmneziaStyle.color.transparent anchors.verticalCenter: parent.verticalCenter
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
border.width: 1 implicitWidth: 56
radius: 16 implicitHeight: 56
} radius: 16
indicator: Rectangle { color: {
id: background if (root.hovered) {
return hoveredColor
anchors.verticalCenter: parent.verticalCenter }
return defaultColor
implicitWidth: 56 }
implicitHeight: 56
radius: 16 Behavior on color {
PropertyAnimation { duration: 200 }
color: { }
if (root.hovered) {
return hoveredColor Rectangle {
} id: imageBorder
return defaultColor
} anchors.centerIn: parent
width: 24
Behavior on color { height: 24
PropertyAnimation { duration: 200 } color: AmneziaStyle.color.transparent
} border.color: root.checked ?
(root.enabled ?
Rectangle { checkedBorderColor :
id: imageBorder checkedBorderDisabledColor) :
defaultBorderColor
anchors.centerIn: parent border.width: 1
width: 24 radius: 4
height: 24
color: AmneziaStyle.color.transparent Image {
border.color: root.checked ? anchors.centerIn: parent
(root.enabled ?
checkedBorderColor : source: root.pressed ? imageSource : root.checked ? imageSource : ""
checkedBorderDisabledColor) : layer {
defaultBorderColor enabled: true
border.width: 1 effect: ColorOverlay {
radius: 4 color: {
if (root.pressed) {
Image { return root.pressedImageColor
anchors.centerIn: parent } else if (root.checked) {
if (root.enabled) {
source: root.pressed ? imageSource : root.checked ? imageSource : "" return root.checkedImageColor
layer { } else {
enabled: true return root.checkedDisabledImageColor
effect: ColorOverlay { }
color: { } else {
if (root.pressed) { return root.defaultImageColor
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
contentItem: Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: 8 + background.width anchors.verticalCenter: parent.verticalCenter
implicitHeight: content.implicitHeight spacing: 4
ColumnLayout { ListItemTitleType {
id: content Layout.fillWidth: true
anchors.left: parent.left text: root.text
anchors.right: parent.right color: root.enabled ? root.textColor : root.textDisabledColor
anchors.verticalCenter: parent.verticalCenter }
spacing: 4 CaptionTextType {
id: description
ListItemTitleType {
Layout.fillWidth: true Layout.fillWidth: true
text: root.text text: root.descriptionText
color: root.enabled ? root.textColor : root.textDisabledColor color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
}
visible: root.descriptionText !== ""
CaptionTextType { }
id: description }
}
Layout.fillWidth: true
MouseArea {
text: root.descriptionText anchors.fill: parent
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor cursorShape: Qt.PointingHandCursor
enabled: false
visible: root.descriptionText !== "" }
}
}
} Keys.onEnterPressed: {
root.checked = !root.checked
MouseArea { }
anchors.fill: parent
cursorShape: Qt.PointingHandCursor Keys.onReturnPressed: {
enabled: false root.checked = !root.checked
} }
}
Keys.onEnterPressed: {
root.checked = !root.checked
}
Keys.onReturnPressed: {
root.checked = !root.checked
}
}

View file

@ -1,328 +1,309 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
property string text // property alias focusObjectName: eyeImage.objectName
property int textMaximumLineCount: 2 property string text
property int textElide: Qt.ElideRight property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight
property string descriptionText
property string descriptionText
property var clickedFunction
property var clickedFunction
property string buttonImageSource
property string rightImageSource property string buttonImageSource
property string leftImageSource property string rightImageSource
property bool isLeftImageHoverEnabled: true property string leftImageSource
property bool isSmallLeftImage: false property bool isLeftImageHoverEnabled: true
property bool isSmallLeftImage: false
property alias rightButton: rightImage
property alias eyeButton: eyeImage property alias rightButton: rightImage
property FlickableType parentFlickable property alias eyeButton: eyeImage
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string descriptionColor: AmneziaStyle.color.mutedGray property string descriptionColor: AmneziaStyle.color.mutedGray
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
property real textOpacity: 1.0 property real textOpacity: 1.0
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property bool descriptionOnTop: false property bool descriptionOnTop: false
property bool hideDescription: true property bool hideDescription: true
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
onFocusChanged: { MouseArea {
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.PointingHandCursor
root.parentFlickable.ensureVisible(root) hoverEnabled: root.enabled
}
} onEntered: {
} if (rightImageSource) {
rightImageBackground.color = rightImage.hoveredColor
Connections { } else if (leftImageSource) {
target: rightImage leftImageBackground.color = rightImage.hoveredColor
function onFocusChanged() { }
if (rightImage.activeFocus) { root.textOpacity = 0.8
if (root.parentFlickable) { }
root.parentFlickable.ensureVisible(root)
} onExited: {
} if (rightImageSource) {
} rightImageBackground.color = rightImage.defaultColor
} } else if (leftImageSource) {
leftImageBackground.color = rightImage.defaultColor
MouseArea { }
anchors.fill: parent root.textOpacity = 1
cursorShape: Qt.PointingHandCursor }
hoverEnabled: root.enabled
onPressedChanged: {
onEntered: { if (rightImageSource) {
if (rightImageSource) { rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
rightImageBackground.color = rightImage.hoveredColor } else if (leftImageSource) {
} else if (leftImageSource) { leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
leftImageBackground.color = rightImage.hoveredColor }
} root.textOpacity = 0.7
root.textOpacity = 0.8 }
}
onClicked: {
onExited: { if (clickedFunction && typeof clickedFunction === "function") {
if (rightImageSource) { clickedFunction()
rightImageBackground.color = rightImage.defaultColor }
} else if (leftImageSource) { }
leftImageBackground.color = rightImage.defaultColor }
}
root.textOpacity = 1 RowLayout {
} id: content
anchors.fill: parent
onPressedChanged: { anchors.leftMargin: 16
if (rightImageSource) { anchors.rightMargin: 16
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor anchors.topMargin: 16
} else if (leftImageSource) { anchors.bottomMargin: 16
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
} Rectangle {
root.textOpacity = 0.7 id: leftImageBackground
}
visible: leftImageSource ? true : false
onClicked: {
if (clickedFunction && typeof clickedFunction === "function") { Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
clickedFunction() Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
} Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
}
} radius: 12
color: AmneziaStyle.color.transparent
RowLayout {
id: content Behavior on color {
anchors.fill: parent PropertyAnimation { duration: 200 }
anchors.leftMargin: 16 }
anchors.rightMargin: 16
anchors.topMargin: 16 Image {
anchors.bottomMargin: 16 id: leftImage
Rectangle { anchors.centerIn: parent
id: leftImageBackground source: leftImageSource
}
visible: leftImageSource ? true : false }
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56 ColumnLayout {
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56 property real textLineHeight: 21.6
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0 property real descriptionTextLineHeight: 16
radius: 12 property int textPixelSize: 18
color: AmneziaStyle.color.transparent property int descriptionTextSize: 13
Behavior on color { ListItemTitleType {
PropertyAnimation { duration: 200 } text: root.text
} color: {
if (root.enabled) {
Image { return root.descriptionOnTop ? root.descriptionColor : root.textColor
id: leftImage } else {
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
anchors.centerIn: parent }
source: leftImageSource }
}
} maximumLineCount: root.textMaximumLineCount
elide: root.textElide
ColumnLayout {
property real textLineHeight: 21.6 opacity: root.textOpacity
property real descriptionTextLineHeight: 16
Layout.fillWidth: true
property int textPixelSize: 18
property int descriptionTextSize: 13 lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
ListItemTitleType { font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
text: root.text
color: { horizontalAlignment: Text.AlignLeft
if (root.enabled) { verticalAlignment: Text.AlignVCenter
return root.descriptionOnTop ? root.descriptionColor : root.textColor
} else { Behavior on opacity {
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor PropertyAnimation { duration: 200 }
} }
} }
maximumLineCount: root.textMaximumLineCount
elide: root.textElide CaptionTextType {
id: description
opacity: root.textOpacity
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
Layout.fillWidth: true color: {
if (root.enabled) {
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight return root.descriptionOnTop ? root.textColor : root.descriptionColor
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize } else {
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0 return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
}
horizontalAlignment: Text.AlignLeft }
verticalAlignment: Text.AlignVCenter
opacity: root.textOpacity
Behavior on opacity {
PropertyAnimation { duration: 200 } visible: root.descriptionText !== ""
}
} Layout.fillWidth: true
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
CaptionTextType { font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
id: description font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText horizontalAlignment: Text.AlignLeft
color: { verticalAlignment: Text.AlignVCenter
if (root.enabled) {
return root.descriptionOnTop ? root.textColor : root.descriptionColor Behavior on opacity {
} else { PropertyAnimation { duration: 200 }
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor }
}
} function replaceWithAsterisks(input) {
return '*'.repeat(input.length)
opacity: root.textOpacity }
}
visible: root.descriptionText !== "" }
Layout.fillWidth: true ImageButtonType {
id: eyeImage
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight visible: buttonImageSource !== ""
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02 implicitWidth: 40
implicitHeight: 40
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter hoverEnabled: true
image: buttonImageSource
Behavior on opacity { imageColor: rightImageColor
PropertyAnimation { duration: 200 }
} Layout.alignment: Qt.AlignRight
function replaceWithAsterisks(input) { Rectangle {
return '*'.repeat(input.length) id: eyeImageBackground
} anchors.fill: parent
} radius: 12
} color: AmneziaStyle.color.transparent
ImageButtonType { Behavior on color {
id: eyeImage PropertyAnimation { duration: 200 }
visible: buttonImageSource !== "" }
}
implicitWidth: 40
implicitHeight: 40 onClicked: {
hideDescription = !hideDescription
hoverEnabled: true }
image: buttonImageSource
imageColor: rightImageColor Keys.onEnterPressed: {
clicked()
Layout.alignment: Qt.AlignRight }
Rectangle { Keys.onReturnPressed: {
id: eyeImageBackground clicked()
anchors.fill: parent }
radius: 12 }
color: AmneziaStyle.color.transparent
ImageButtonType {
Behavior on color { id: rightImage
PropertyAnimation { duration: 200 }
} implicitWidth: 40
} implicitHeight: 40
onClicked: { hoverEnabled: false
hideDescription = !hideDescription image: rightImageSource
} imageColor: rightImageColor
visible: rightImageSource ? true : false
Keys.onEnterPressed: {
clicked() Layout.alignment: Qt.AlignRight
}
Rectangle {
Keys.onReturnPressed: { id: rightImageBackground
clicked() anchors.fill: parent
} radius: 12
} color: AmneziaStyle.color.transparent
ImageButtonType { Behavior on color {
id: rightImage PropertyAnimation { duration: 200 }
}
implicitWidth: 40 }
implicitHeight: 40 onClicked: {
if (clickedFunction && typeof clickedFunction === "function") {
hoverEnabled: false clickedFunction()
image: rightImageSource }
imageColor: rightImageColor }
visible: rightImageSource ? true : false }
}
Layout.alignment: Qt.AlignRight
Rectangle {
Rectangle { id: background
id: rightImageBackground anchors.fill: root
anchors.fill: parent color: AmneziaStyle.color.transparent
radius: 12
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 }
} Behavior on color {
} PropertyAnimation { duration: 200 }
onClicked: { }
if (clickedFunction && typeof clickedFunction === "function") { }
clickedFunction()
} Keys.onEnterPressed: {
} if (clickedFunction && typeof clickedFunction === "function") {
} clickedFunction()
} }
}
Rectangle {
id: background Keys.onReturnPressed: {
anchors.fill: root if (clickedFunction && typeof clickedFunction === "function") {
color: AmneziaStyle.color.transparent clickedFunction()
}
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()
}
}
}

View file

@ -6,33 +6,8 @@ ListView {
property bool isFocusable: true property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {} ScrollBar.vertical: ScrollBarType {}
clip: true clip: true
reuseItems: true reuseItems: true
snapMode: ListView.SnapToItem
} }

View file

@ -6,7 +6,7 @@ import Style 1.0
import "TextTypes" import "TextTypes"
ListView { ListViewType {
id: root id: root
property var rootWidth property var rootWidth
@ -25,13 +25,6 @@ ListView {
width: rootWidth width: rootWidth
height: root.contentItem.height height: root.contentItem.height
clip: true
reuseItems: true
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
ButtonGroup { ButtonGroup {
id: buttonGroup id: buttonGroup
} }

View file

@ -1,172 +1,162 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Switch { Switch {
id: root id: root
property alias descriptionText: description.text property alias descriptionText: description.text
property string descriptionTextColor: AmneziaStyle.color.mutedGray property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray property string textDisabledColor: AmneziaStyle.color.mutedGray
property string checkedIndicatorColor: AmneziaStyle.color.richBrown property string checkedIndicatorColor: AmneziaStyle.color.richBrown
property string defaultIndicatorColor: AmneziaStyle.color.transparent property string defaultIndicatorColor: AmneziaStyle.color.transparent
property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorColor: AmneziaStyle.color.deepBrown
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown property string checkedIndicatorBorderColor: AmneziaStyle.color.richBrown
property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray property string defaultIndicatorBorderColor: AmneziaStyle.color.charcoalGray
property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown property string checkedDisabledIndicatorBorderColor: AmneziaStyle.color.deepBrown
property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot property string checkedInnerCircleColor: AmneziaStyle.color.goldenApricot
property string defaultInnerCircleColor: AmneziaStyle.color.paleGray property string defaultInnerCircleColor: AmneziaStyle.color.paleGray
property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown property string checkedDisabledInnerCircleColor: AmneziaStyle.color.mutedBrown
property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray property string defaultDisabledInnerCircleColor: AmneziaStyle.color.charcoalGray
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
property bool isFocusable: true property bool isFocusable: true
Keys.onTabPressed: { Keys.onTabPressed: {
FocusController.nextKeyTabItem() FocusController.nextKeyTabItem()
} }
Keys.onBacktabPressed: { Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: { Keys.onUpPressed: {
FocusController.nextKeyUpItem() FocusController.nextKeyUpItem()
} }
Keys.onDownPressed: { Keys.onDownPressed: {
FocusController.nextKeyDownItem() FocusController.nextKeyDownItem()
} }
Keys.onLeftPressed: { Keys.onLeftPressed: {
FocusController.nextKeyLeftItem() FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: { Keys.onRightPressed: {
FocusController.nextKeyRightItem() FocusController.nextKeyRightItem()
} }
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus focusPolicy: Qt.TabFocus
property FlickableType parentFlickable: null indicator: Rectangle {
id: switcher
onFocusChanged: {
if (root.activeFocus) { anchors.right: parent.right
if (root.parentFlickable) { anchors.verticalCenter: parent.verticalCenter
root.parentFlickable.ensureVisible(root)
} implicitWidth: 52
} implicitHeight: 32
}
radius: 16
indicator: Rectangle { color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
id: switcher : root.defaultIndicatorColor
anchors.right: parent.right border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
anchors.verticalCenter: parent.verticalCenter : root.defaultIndicatorBorderColor)
implicitWidth: 52 Behavior on color {
implicitHeight: 32 PropertyAnimation { duration: 200 }
}
radius: 16 Behavior on border.color {
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor) PropertyAnimation { duration: 200 }
: root.defaultIndicatorColor }
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor) Rectangle {
: root.defaultIndicatorBorderColor) id: innerCircle
Behavior on color { anchors.verticalCenter: parent.verticalCenter
PropertyAnimation { duration: 200 } x: root.checked ? parent.width - width - 4 : 8
} width: root.checked ? 24 : 16
Behavior on border.color { height: root.checked ? 24 : 16
PropertyAnimation { duration: 200 } radius: 23
} color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor)
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor)
Rectangle {
id: innerCircle Behavior on x {
PropertyAnimation { duration: 200 }
anchors.verticalCenter: parent.verticalCenter }
x: root.checked ? parent.width - width - 4 : 8 }
width: root.checked ? 24 : 16
height: root.checked ? 24 : 16 Rectangle {
radius: 23 anchors.centerIn: innerCircle
color: root.checked ? (root.enabled ? root.checkedInnerCircleColor : root.checkedDisabledInnerCircleColor) width: 40
: (root.enabled ? root.defaultInnerCircleColor : root.defaultDisabledInnerCircleColor) height: 40
radius: 23
Behavior on x { color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
PropertyAnimation { duration: 200 }
} Behavior on color {
} PropertyAnimation { duration: 200 }
}
Rectangle { }
anchors.centerIn: innerCircle }
width: 40
height: 40 contentItem: ColumnLayout {
radius: 23 id: content
color: root.hovered ? root.hoveredIndicatorBackgroundColor : root.defaultIndicatorBackgroundColor
anchors.verticalCenter: parent.verticalCenter
Behavior on color { anchors.left: parent.left
PropertyAnimation { duration: 200 }
} ListItemTitleType {
} Layout.fillWidth: true
} rightPadding: indicator.width
contentItem: ColumnLayout { text: root.text
id: content color: root.enabled ? root.textColor : root.textDisabledColor
}
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left CaptionTextType {
id: description
ListItemTitleType {
Layout.fillWidth: true Layout.fillWidth: true
rightPadding: indicator.width rightPadding: indicator.width
text: root.text color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
color: root.enabled ? root.textColor : root.textDisabledColor
} visible: text !== ""
}
CaptionTextType { }
id: description
MouseArea {
Layout.fillWidth: true anchors.fill: parent
rightPadding: indicator.width cursorShape: Qt.PointingHandCursor
enabled: false
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor }
visible: text !== "" Keys.onEnterPressed: event => handleSwitch(event)
} Keys.onReturnPressed: event => handleSwitch(event)
} Keys.onSpacePressed: event => handleSwitch(event)
MouseArea { function handleSwitch(event) {
anchors.fill: parent if (!event.isAutoRepeat) {
cursorShape: Qt.PointingHandCursor root.checked = !root.checked
enabled: false root.checkedChanged()
} }
event.accepted = true
Keys.onEnterPressed: event => handleSwitch(event) }
Keys.onReturnPressed: event => handleSwitch(event) }
Keys.onSpacePressed: event => handleSwitch(event)
function handleSwitch(event) {
if (!event.isAutoRepeat) {
root.checked = !root.checked
root.checkedChanged()
}
event.accepted = true
}
}

View file

@ -1,118 +1,109 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Style 1.0 import Style 1.0
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null MouseArea {
onFocusChanged: { id: parentMouse
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.IBeamCursor
root.parentFlickable.ensureVisible(root) onClicked: textArea.forceActiveFocus()
} hoverEnabled: true
}
} FlickableType {
id: fl
MouseArea { interactive: false
id: parentMouse
anchors.fill: parent anchors.top: parent.top
cursorShape: Qt.IBeamCursor anchors.bottom: parent.bottom
onClicked: textArea.forceActiveFocus() contentHeight: textArea.implicitHeight
hoverEnabled: true TextArea {
id: textArea
FlickableType {
id: fl width: parent.width
interactive: false
topPadding: 16
anchors.top: parent.top leftPadding: 16
anchors.bottom: parent.bottom anchors.topMargin: 16
contentHeight: textArea.implicitHeight anchors.bottomMargin: 16
TextArea {
id: textArea color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
width: parent.width selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray
topPadding: 16
leftPadding: 16 font.pixelSize: 16
anchors.topMargin: 16 font.weight: Font.Medium
anchors.bottomMargin: 16 font.family: "PT Root UI VF"
color: AmneziaStyle.color.paleGray placeholderText: root.placeholderText
selectionColor: AmneziaStyle.color.richBrown text: root.text
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray onCursorVisibleChanged: {
if (textArea.cursorVisible) {
font.pixelSize: 16 fl.interactive = true
font.weight: Font.Medium } else {
font.family: "PT Root UI VF" fl.interactive = false
}
placeholderText: root.placeholderText }
text: root.text
wrapMode: Text.Wrap
onCursorVisibleChanged: {
if (textArea.cursorVisible) { MouseArea {
fl.interactive = true id: textAreaMouse
} else { anchors.fill: parent
fl.interactive = false acceptedButtons: Qt.RightButton
} hoverEnabled: true
} onClicked: {
fl.interactive = true
wrapMode: Text.Wrap contextMenu.open()
}
MouseArea { }
id: textAreaMouse
anchors.fill: parent onFocusChanged: {
acceptedButtons: Qt.RightButton root.border.color = getBorderColor(borderNormalColor)
hoverEnabled: true }
onClicked: {
fl.interactive = true ContextMenuType {
contextMenu.open() id: contextMenu
} textObj: textArea
} }
}
onFocusChanged: { }
root.border.color = getBorderColor(borderNormalColor)
} onPressed: {
root.border.color = getBorderColor(borderFocusedColor)
ContextMenuType { }
id: contextMenu
textObj: textArea onExited: {
} root.border.color = getBorderColor(borderNormalColor)
} }
}
onEntered: {
onPressed: { root.border.color = getBorderColor(borderHoveredColor)
root.border.color = getBorderColor(borderFocusedColor) }
} }
onExited: {
root.border.color = getBorderColor(borderNormalColor) function getBorderColor(noneFocusedColor) {
} return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
onEntered: { }
root.border.color = getBorderColor(borderHoveredColor)
}
}
function getBorderColor(noneFocusedColor) {
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
}

View file

@ -1,180 +1,171 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Rectangle { Rectangle {
id: root id: root
property string placeholderText property string placeholderText
property string text property string text
property string headerText property string headerText
property alias textArea: textArea property alias textArea: textArea
property alias textAreaText: textArea.text property alias textAreaText: textArea.text
property string borderHoveredColor: AmneziaStyle.color.charcoalGray property string borderHoveredColor: AmneziaStyle.color.charcoalGray
property string borderNormalColor: AmneziaStyle.color.slateGray property string borderNormalColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string firstButtonImage property string firstButtonImage
property string secondButtonImage property string secondButtonImage
property var firstButtonClickedFunc property var firstButtonClickedFunc
property var secondButtonClickedFunc property var secondButtonClickedFunc
height: 148 height: 148
color: AmneziaStyle.color.onyxBlack color: AmneziaStyle.color.onyxBlack
border.width: 1 border.width: 1
border.color: getBorderColor(borderNormalColor) border.color: getBorderColor(borderNormalColor)
radius: 16 radius: 16
property FlickableType parentFlickable: null MouseArea {
onFocusChanged: { id: parentMouse
if (root.activeFocus) { anchors.fill: parent
if (root.parentFlickable) { cursorShape: Qt.IBeamCursor
root.parentFlickable.ensureVisible(root) onClicked: textArea.forceActiveFocus()
} hoverEnabled: true
}
} ColumnLayout {
anchors.fill: parent
MouseArea { anchors.margins: 16
id: parentMouse spacing: 0
anchors.fill: parent
cursorShape: Qt.IBeamCursor LabelTextType {
onClicked: textArea.forceActiveFocus() Layout.fillWidth: true
hoverEnabled: true text: root.headerText
}
ColumnLayout {
anchors.fill: parent TextArea {
anchors.margins: 16 id: textArea
spacing: 0
Layout.fillWidth: true
LabelTextType { Layout.fillHeight: true
Layout.fillWidth: true
text: root.headerText leftPadding: 0
} Layout.bottomMargin: 16
TextArea { color: AmneziaStyle.color.paleGray
id: textArea selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
Layout.fillWidth: true placeholderTextColor: AmneziaStyle.color.mutedGray
Layout.fillHeight: true
font.pixelSize: 16
leftPadding: 0 font.weight: Font.Medium
Layout.bottomMargin: 16 font.family: "PT Root UI VF"
color: AmneziaStyle.color.paleGray placeholderText: root.placeholderText
selectionColor: AmneziaStyle.color.richBrown text: root.text
selectedTextColor: AmneziaStyle.color.paleGray
placeholderTextColor: AmneziaStyle.color.mutedGray onCursorVisibleChanged: {
if (textArea.cursorVisible) {
font.pixelSize: 16 fl.interactive = true
font.weight: Font.Medium } else {
font.family: "PT Root UI VF" fl.interactive = false
}
placeholderText: root.placeholderText }
text: root.text
wrapMode: Text.Wrap
onCursorVisibleChanged: {
if (textArea.cursorVisible) { MouseArea {
fl.interactive = true id: textAreaMouse
} else { anchors.fill: parent
fl.interactive = false acceptedButtons: Qt.RightButton
} hoverEnabled: true
} onClicked: {
fl.interactive = true
wrapMode: Text.Wrap contextMenu.open()
}
MouseArea { }
id: textAreaMouse
anchors.fill: parent onFocusChanged: {
acceptedButtons: Qt.RightButton root.border.color = getBorderColor(borderNormalColor)
hoverEnabled: true }
onClicked: {
fl.interactive = true ContextMenuType {
contextMenu.open() id: contextMenu
} textObj: textArea
} }
}
onFocusChanged: {
root.border.color = getBorderColor(borderNormalColor) RowLayout {
} Layout.fillWidth: true
Layout.leftMargin: -8
ContextMenuType { spacing: 0
id: contextMenu ImageButtonType {
textObj: textArea id: firstButton
} visible: root.firstButtonImage !== ""
}
imageColor: AmneziaStyle.color.paleGray
RowLayout {
Layout.fillWidth: true image: root.firstButtonImage
Layout.leftMargin: -8 onClicked: function() {
spacing: 0 if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
ImageButtonType { root.firstButtonClickedFunc()
id: firstButton }
visible: root.firstButtonImage !== "" }
}
imageColor: AmneziaStyle.color.paleGray
ImageButtonType {
image: root.firstButtonImage id: secondButton
onClicked: function() { visible: root.secondButtonImage !== ""
if (root.firstButtonClickedFunc && typeof root.firstButtonClickedFunc === "function") {
root.firstButtonClickedFunc() imageColor: AmneziaStyle.color.paleGray
}
} image: root.secondButtonImage
} onClicked: function() {
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
ImageButtonType { root.secondButtonClickedFunc()
id: secondButton }
visible: root.secondButtonImage !== "" }
}
imageColor: AmneziaStyle.color.paleGray
Item {
image: root.secondButtonImage Layout.fillWidth: true
onClicked: function() { }
if (root.secondButtonClickedFunc && typeof root.secondButtonClickedFunc === "function") {
root.secondButtonClickedFunc() ImageButtonType {
} id: resetButton
} imageColor: AmneziaStyle.color.paleGray
}
visible: root.textAreaText !== ""
Item { image: "qrc:/images/controls/close.svg"
Layout.fillWidth: true
} onClicked: function() {
root.textAreaText = ""
ImageButtonType { textArea.focus = true
id: resetButton }
imageColor: AmneziaStyle.color.paleGray }
}
visible: root.textAreaText !== "" }
image: "qrc:/images/controls/close.svg"
onPressed: {
onClicked: function() { root.border.color = getBorderColor(borderFocusedColor)
root.textAreaText = "" }
textArea.focus = true
} onExited: {
} root.border.color = getBorderColor(borderNormalColor)
} }
}
onEntered: {
onPressed: { root.border.color = getBorderColor(borderHoveredColor)
root.border.color = getBorderColor(borderFocusedColor) }
} }
onExited: {
root.border.color = getBorderColor(borderNormalColor) function getBorderColor(noneFocusedColor) {
} return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
onEntered: { }
root.border.color = getBorderColor(borderHoveredColor)
}
}
function getBorderColor(noneFocusedColor) {
return textArea.focus ? root.borderFocusedColor : noneFocusedColor
}
}

View file

@ -1,233 +1,220 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
Item { Item {
id: root id: root
property string headerText property string headerText
property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray property string headerTextDisabledColor: AmneziaStyle.color.charcoalGray
property string headerTextColor: AmneziaStyle.color.mutedGray property string headerTextColor: AmneziaStyle.color.mutedGray
property alias errorText: errorField.text property alias errorText: errorField.text
property bool checkEmptyText: false property bool checkEmptyText: false
property bool rightButtonClickedOnEnter: false property bool rightButtonClickedOnEnter: false
property string buttonText property string buttonText
property string buttonImageSource property string buttonImageSource
property var clickedFunc property var clickedFunc
property alias textField: textField property alias textField: textField
property string textFieldTextColor: AmneziaStyle.color.paleGray property string textFieldTextColor: AmneziaStyle.color.paleGray
property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray property string textFieldTextDisabledColor: AmneziaStyle.color.mutedGray
property bool textFieldEditable: true property bool textFieldEditable: true
property string borderColor: AmneziaStyle.color.slateGray property string borderColor: AmneziaStyle.color.slateGray
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property string backgroundColor: AmneziaStyle.color.onyxBlack property string backgroundColor: AmneziaStyle.color.onyxBlack
property string backgroundDisabledColor: AmneziaStyle.color.transparent property string backgroundDisabledColor: AmneziaStyle.color.transparent
property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray property string bgBorderHoveredColor: AmneziaStyle.color.charcoalGray
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
property FlickableType parentFlickable ColumnLayout {
id: content
Connections { anchors.fill: parent
target: textField
function onFocusChanged() { Rectangle {
if (textField.activeFocus) { id: backgroud
if (root.parentFlickable) { Layout.fillWidth: true
root.parentFlickable.ensureVisible(root) Layout.preferredHeight: input.implicitHeight
} color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
} radius: 16
} border.color: getBackgroundBorderColor(root.borderColor)
} border.width: 1
ColumnLayout { Behavior on border.color {
id: content PropertyAnimation { duration: 200 }
anchors.fill: parent }
Rectangle { RowLayout {
id: backgroud id: input
Layout.fillWidth: true anchors.fill: backgroud
Layout.preferredHeight: input.implicitHeight ColumnLayout {
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor Layout.margins: 16
radius: 16 LabelTextType {
border.color: getBackgroundBorderColor(root.borderColor) text: root.headerText
border.width: 1 color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor
Behavior on border.color { visible: text !== ""
PropertyAnimation { duration: 200 }
} Layout.fillWidth: true
}
RowLayout {
id: input TextField {
anchors.fill: backgroud id: textField
ColumnLayout {
Layout.margins: 16 property bool isFocusable: true
LabelTextType {
text: root.headerText Keys.onTabPressed: {
color: root.enabled ? root.headerTextColor : root.headerTextDisabledColor FocusController.nextKeyTabItem()
}
visible: text !== ""
Keys.onBacktabPressed: {
Layout.fillWidth: true FocusController.previousKeyTabItem()
} }
TextField { enabled: root.textFieldEditable
id: textField color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
property bool isFocusable: true inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
Keys.onTabPressed: { placeholderTextColor: AmneziaStyle.color.charcoalGray
FocusController.nextKeyTabItem()
} selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem() font.pixelSize: 16
} font.weight: 400
font.family: "PT Root UI VF"
enabled: root.textFieldEditable
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor height: 24
Layout.fillWidth: true
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
topPadding: 0
placeholderTextColor: AmneziaStyle.color.charcoalGray rightPadding: 0
leftPadding: 0
selectionColor: AmneziaStyle.color.richBrown bottomPadding: 0
selectedTextColor: AmneziaStyle.color.paleGray
background: Rectangle {
font.pixelSize: 16 anchors.fill: parent
font.weight: 400 color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor
font.family: "PT Root UI VF" }
height: 24 onTextChanged: {
Layout.fillWidth: true root.errorText = ""
}
topPadding: 0
rightPadding: 0 onActiveFocusChanged: {
leftPadding: 0 if (root.checkEmptyText && text === "") {
bottomPadding: 0 root.errorText = qsTr("The field can't be empty")
}
background: Rectangle { }
anchors.fill: parent
color: root.enabled ? root.backgroundColor : root.backgroundDisabledColor MouseArea {
} anchors.fill: parent
acceptedButtons: Qt.RightButton
onTextChanged: { onClicked: contextMenu.open()
root.errorText = "" enabled: true
} }
onActiveFocusChanged: { ContextMenuType {
if (root.checkEmptyText && text === "") { id: contextMenu
root.errorText = qsTr("The field can't be empty") textObj: textField
} }
}
onFocusChanged: {
MouseArea { backgroud.border.color = getBackgroundBorderColor(root.borderColor)
anchors.fill: parent }
acceptedButtons: Qt.RightButton }
onClicked: contextMenu.open() }
enabled: true }
} }
ContextMenuType { SmallTextType {
id: contextMenu id: errorField
textObj: textField
} text: root.errorText
visible: root.errorText !== ""
onFocusChanged: { color: AmneziaStyle.color.vibrantRed
backgroud.border.color = getBackgroundBorderColor(root.borderColor)
} Layout.fillWidth: true
} }
} }
}
} MouseArea {
anchors.fill: root
SmallTextType { cursorShape: Qt.IBeamCursor
id: errorField
hoverEnabled: true
text: root.errorText
visible: root.errorText !== "" onPressed: function(mouse) {
color: AmneziaStyle.color.vibrantRed textField.forceActiveFocus()
mouse.accepted = false
Layout.fillWidth: true
} backgroud.border.color = getBackgroundBorderColor(root.borderColor)
} }
MouseArea { onEntered: {
anchors.fill: root backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor)
cursorShape: Qt.IBeamCursor }
hoverEnabled: true
onExited: {
onPressed: function(mouse) { backgroud.border.color = getBackgroundBorderColor(root.borderColor)
textField.forceActiveFocus() }
mouse.accepted = false }
backgroud.border.color = getBackgroundBorderColor(root.borderColor) BasicButtonType {
} visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
onEntered: { focusPolicy: Qt.NoFocus
backgroud.border.color = getBackgroundBorderColor(bgBorderHoveredColor) text: root.buttonText
} leftImageSource: root.buttonImageSource
anchors.top: content.top
onExited: { anchors.bottom: content.bottom
backgroud.border.color = getBackgroundBorderColor(root.borderColor) anchors.right: content.right
}
} height: content.implicitHeight
width: content.implicitHeight
BasicButtonType { squareLeftSide: true
visible: (root.buttonText !== "") || (root.buttonImageSource !== "")
clickedFunc: function() {
focusPolicy: Qt.NoFocus if (root.clickedFunc && typeof root.clickedFunc === "function") {
text: root.buttonText root.clickedFunc()
leftImageSource: root.buttonImageSource }
}
anchors.top: content.top }
anchors.bottom: content.bottom
anchors.right: content.right function getBackgroundBorderColor(noneFocusedColor) {
return textField.focus ? root.borderFocusedColor : noneFocusedColor
height: content.implicitHeight }
width: content.implicitHeight
squareLeftSide: true Keys.onEnterPressed: {
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc: function() { clickedFunc()
if (root.clickedFunc && typeof root.clickedFunc === "function") { }
root.clickedFunc()
} // if (KeyNavigation.tab) {
} // KeyNavigation.tab.forceActiveFocus();
} // }
}
function getBackgroundBorderColor(noneFocusedColor) {
return textField.focus ? root.borderFocusedColor : noneFocusedColor Keys.onReturnPressed: {
} if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
Keys.onEnterPressed: { }
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc() // if (KeyNavigation.tab) {
} // KeyNavigation.tab.forceActiveFocus();
// }
// if (KeyNavigation.tab) { }
// KeyNavigation.tab.forceActiveFocus(); }
// }
}
Keys.onReturnPressed: {
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
}
// if (KeyNavigation.tab) {
// KeyNavigation.tab.forceActiveFocus();
// }
}
}

View file

@ -20,7 +20,9 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
sourceModel: ServersModel sourceModel: ServersModel
filters: [ filters: [
ValueFilter { ValueFilter {
roleName: "isCurrentlyProcessed" roleName: "isCurrentlyProcessed"
@ -29,67 +31,55 @@ PageType {
] ]
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column { spacing: 16
id: content
anchors.top: parent.top model: proxyServersModel
anchors.left: parent.left
anchors.right: parent.right
spacing: 16 delegate: ColumnLayout {
width: listView.width
Repeater { BaseHeaderType {
model: proxyServersModel Layout.fillWidth: true
delegate: Item { Layout.topMargin: 20
implicitWidth: parent.width Layout.leftMargin: 16
implicitHeight: delegateContent.implicitHeight Layout.rightMargin: 16
ColumnLayout { headerText: qsTr("Removing services from %1").arg(name)
id: delegateContent }
anchors.fill: parent ProgressBarType {
anchors.rightMargin: 16 id: progressBar
anchors.leftMargin: 16
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 32
Layout.topMargin: 20 Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Removing services from %1").arg(name) Timer {
} id: timer
ProgressBarType { interval: 300
id: progressBar repeat: true
running: true
Layout.fillWidth: true onTriggered: {
Layout.topMargin: 32 progressBar.value += 0.003
Timer {
id: timer
interval: 300
repeat: true
running: true
onTriggered: {
progressBar.value += 0.003
}
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
text: qsTr("Usually it takes no more than 5 minutes")
}
} }
} }
} }
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Usually it takes no more than 5 minutes")
}
} }
} }
} }

View file

@ -25,23 +25,17 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
@ -50,16 +44,14 @@ PageType {
} }
} }
model: 1 model: 1 // fake model to force the ListView to be created without a model
clip: true
spacing: 16 spacing: 16
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: passwordTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -87,8 +79,6 @@ PageType {
width: listView.width width: listView.width
SwitcherType { SwitcherType {
id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -30,233 +30,223 @@ PageType {
} }
} }
ListView { ListViewType {
id: listview id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: saveButton.top anchors.bottom: saveButton.top
width: parent.width width: parent.width
clip: true
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: AwgConfigModel model: AwgConfigModel
delegate: Item { delegate: ColumnLayout {
id: delegateItem width: listView.width
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" && property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" && junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" && junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" junkPacketCountTextField.errorText === ""
ColumnLayout { spacing: 0
id: col
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.leftMargin: 16 headerText: qsTr("AmneziaWG settings")
anchors.rightMargin: 16 }
spacing: 0 TextFieldWithHeaderType {
id: mtuTextField
BaseHeaderType { Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("AmneziaWG settings") headerText: qsTr("MTU")
} textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
TextFieldWithHeaderType { textField.onEditingFinished: {
id: mtuTextField if (textField.text !== clientMtu) {
Layout.fillWidth: true clientMtu = textField.text
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
} }
checkEmptyText: true
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: junkPacketCountTextField id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jc - Junk packet count" Layout.fillWidth: true
textField.text: clientJunkPacketCount Layout.topMargin: 16
textField.validator: IntValidator { bottom: 0 } Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { headerText: "Jc - Junk packet count"
if (textField.text !== clientJunkPacketCount) { textField.text: clientJunkPacketCount
clientJunkPacketCount = textField.text textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketCount) {
clientJunkPacketCount = textField.text
} }
checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
} }
TextFieldWithHeaderType { checkEmptyText: true
id: junkPacketMinSizeTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmin - Junk packet minimum size" TextFieldWithHeaderType {
textField.text: clientJunkPacketMinSize id: junkPacketMinSizeTextField
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientJunkPacketMinSize) { Layout.topMargin: 16
clientJunkPacketMinSize = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
headerText: "Jmin - Junk packet minimum size"
textField.text: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textField.text
} }
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
} }
TextFieldWithHeaderType { checkEmptyText: true
id: junkPacketMaxSizeTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmax - Junk packet maximum size" TextFieldWithHeaderType {
textField.text: clientJunkPacketMaxSize id: junkPacketMaxSizeTextField
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: { Layout.fillWidth: true
if (textField.text !== clientJunkPacketMaxSize) { Layout.topMargin: 16
clientJunkPacketMaxSize = textField.text Layout.leftMargin: 16
} Layout.rightMargin: 16
headerText: "Jmax - Junk packet maximum size"
textField.text: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textField.text
} }
checkEmptyText: true
} }
Header2TextType { checkEmptyText: true
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings") }
}
TextFieldWithHeaderType { Header2TextType {
id: portTextField Layout.fillWidth: true
Layout.fillWidth: true Layout.topMargin: 16
Layout.topMargin: 8 Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false text: qsTr("Server settings")
}
headerText: qsTr("Port") TextFieldWithHeaderType {
textField.text: port id: portTextField
}
TextFieldWithHeaderType { Layout.fillWidth: true
id: initPacketJunkSizeTextField Layout.topMargin: 8
Layout.fillWidth: true Layout.leftMargin: 16
Layout.topMargin: 16 Layout.rightMargin: 16
enabled: false enabled: false
headerText: "S1 - Init packet junk size" headerText: qsTr("Port")
textField.text: serverInitPacketJunkSize textField.text: port
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "S2 - Response packet junk size" enabled: false
textField.text: serverResponsePacketJunkSize
}
TextFieldWithHeaderType { headerText: "S1 - Init packet junk size"
id: initPacketMagicHeaderTextField textField.text: serverInitPacketJunkSize
Layout.fillWidth: true }
Layout.topMargin: 16
enabled: false TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
headerText: "H1 - Init packet magic header" Layout.fillWidth: true
textField.text: serverInitPacketMagicHeader Layout.topMargin: 16
} Layout.leftMargin: 16
Layout.rightMargin: 16
TextFieldWithHeaderType { enabled: false
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false headerText: "S2 - Response packet junk size"
textField.text: serverResponsePacketJunkSize
}
headerText: "H2 - Response packet magic header" TextFieldWithHeaderType {
textField.text: serverResponsePacketMagicHeader id: initPacketMagicHeaderTextField
}
TextFieldWithHeaderType { Layout.fillWidth: true
id: underloadPacketMagicHeaderTextField Layout.topMargin: 16
Layout.fillWidth: true Layout.leftMargin: 16
Layout.topMargin: 16 Layout.rightMargin: 16
enabled: false enabled: false
headerText: "H3 - Underload packet magic header" headerText: "H1 - Init packet magic header"
textField.text: serverUnderloadPacketMagicHeader textField.text: serverInitPacketMagicHeader
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: "H4 - Transport packet magic header" enabled: false
textField.text: serverTransportPacketMagicHeader
} headerText: "H2 - Response packet magic header"
textField.text: serverResponsePacketMagicHeader
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H3 - Underload packet magic header"
textField.text: serverUnderloadPacketMagicHeader
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: false
headerText: "H4 - Transport packet magic header"
textField.text: serverTransportPacketMagicHeader
} }
} }
} }
@ -273,18 +263,17 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
onActiveFocusChanged: { onActiveFocusChanged: {
if(activeFocus) { if(activeFocus) {
listview.positionViewAtEnd() listView.positionViewAtEnd()
} }
} }
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")
var descriptionText = qsTr("Only the settings for this device will be changed") var descriptionText = qsTr("Only the settings for this device will be changed")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@ -299,11 +288,9 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig()) InstallController.updateContainer(AwgConfigModel.getConfig())
} }
var noButtonFunction = function() {
if (!GC.isMobile()) { var noButtonFunction = function() {}
saveButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }

View file

@ -33,356 +33,339 @@ PageType {
} }
} }
ListView { ListViewType {
id: listview id: listView
property bool isFocusable: true
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
clip: true
model: AwgConfigModel model: AwgConfigModel
delegate: Item { delegate: ColumnLayout {
id: delegateItem id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight width: listView.width
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
ColumnLayout { spacing: 0
id: col
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.leftMargin: 16 headerText: qsTr("AmneziaWG settings")
anchors.rightMargin: 16 }
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 { headerText: qsTr("VPN address subnet")
id: vpnAddressSubnetTextField textField.text: subnetAddress
Layout.fillWidth: true textField.onEditingFinished: {
Layout.topMargin: 40 if (textField.text !== subnetAddress) {
subnetAddress = textField.text
enabled: delegateItem.isEnabled
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress
textField.onEditingFinished: {
if (textField.text !== subnetAddress) {
subnetAddress = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jc - Junk packet count")
textField.text: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text === "") {
textField.text = "0"
}
if (textField.text !== serverJunkPacketCount) {
serverJunkPacketCount = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: junkPacketMinSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size")
textField.text: serverJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size")
textField.text: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S1 - Init packet junk size")
textField.text: serverInitPacketJunkSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverInitPacketJunkSize) {
serverInitPacketJunkSize = textField.text
}
}
checkEmptyText: true
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
} }
} }
TextFieldWithHeaderType { checkEmptyText: true
id: responsePacketJunkSizeTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S2 - Response packet junk size") TextFieldWithHeaderType {
textField.text: serverResponsePacketJunkSize id: portTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { enabled: delegateItem.isEnabled
if (textField.text !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textField.text
}
}
checkEmptyText: true headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
onActiveFocusChanged: { textField.onEditingFinished: {
if(activeFocus) { if (textField.text !== port) {
listview.positionViewAtEnd() port = textField.text
}
} }
} }
TextFieldWithHeaderType { checkEmptyText: true
id: initPacketMagicHeaderTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H1 - Init packet magic header") TextFieldWithHeaderType {
textField.text: serverInitPacketMagicHeader id: junkPacketCountTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { headerText: qsTr("Jc - Junk packet count")
if (textField.text !== serverInitPacketMagicHeader) { textField.text: serverJunkPacketCount
serverInitPacketMagicHeader = textField.text textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textField.text === "") {
textField.text = "0"
} }
checkEmptyText: true if (textField.text !== serverJunkPacketCount) {
serverJunkPacketCount = textField.text
}
} }
TextFieldWithHeaderType { checkEmptyText: true
id: responsePacketMagicHeaderTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H2 - Response packet magic header") TextFieldWithHeaderType {
textField.text: serverResponsePacketMagicHeader id: junkPacketMinSizeTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { headerText: qsTr("Jmin - Junk packet minimum size")
if (textField.text !== serverResponsePacketMagicHeader) { textField.text: serverJunkPacketMinSize
serverResponsePacketMagicHeader = textField.text textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textField.text
} }
checkEmptyText: true
} }
TextFieldWithHeaderType { checkEmptyText: true
id: transportPacketMagicHeaderTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H4 - Transport packet magic header") TextFieldWithHeaderType {
textField.text: serverTransportPacketMagicHeader id: junkPacketMaxSizeTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { headerText: qsTr("Jmax - Junk packet maximum size")
if (textField.text !== serverTransportPacketMagicHeader) { textField.text: serverJunkPacketMaxSize
serverTransportPacketMagicHeader = textField.text textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textField.text !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textField.text
} }
checkEmptyText: true
} }
TextFieldWithHeaderType { checkEmptyText: true
id: underloadPacketMagicHeaderTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H3 - Underload packet magic header") TextFieldWithHeaderType {
textField.text: serverUnderloadPacketMagicHeader id: initPacketJunkSizeTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textField.onEditingFinished: { headerText: qsTr("S1 - Init packet junk size")
if (textField.text !== serverUnderloadPacketMagicHeader) { textField.text: serverInitPacketJunkSize
serverUnderloadPacketMagicHeader = textField.text textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textField.text !== serverInitPacketJunkSize) {
serverInitPacketJunkSize = textField.text
} }
checkEmptyText: true
} }
BasicButtonType { checkEmptyText: true
id: saveRestartButton
Layout.fillWidth: true onActiveFocusChanged: {
Layout.topMargin: 24 if(activeFocus) {
Layout.bottomMargin: 24 listview.positionViewAtEnd()
}
}
}
enabled: underloadPacketMagicHeaderTextField.errorText === "" && TextFieldWithHeaderType {
transportPacketMagicHeaderTextField.errorText === "" && id: responsePacketJunkSizeTextField
responsePacketMagicHeaderTextField.errorText === "" && Layout.fillWidth: true
initPacketMagicHeaderTextField.errorText === "" && Layout.topMargin: 16
responsePacketJunkSizeTextField.errorText === "" && Layout.leftMargin: 16
initPacketJunkSizeTextField.errorText === "" && Layout.rightMargin: 16
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save") headerText: qsTr("S2 - Response packet junk size")
textField.text: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
onActiveFocusChanged: { textField.onEditingFinished: {
if(activeFocus) { if (textField.text !== serverResponsePacketJunkSize) {
listview.positionViewAtEnd() serverResponsePacketJunkSize = textField.text
}
}
checkEmptyText: true
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H1 - Init packet magic header")
textField.text: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H2 - Response packet magic header")
textField.text: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H4 - Transport packet magic header")
textField.text: serverTransportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverTransportPacketMagicHeader) {
serverTransportPacketMagicHeader = textField.text
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("H3 - Underload packet magic header")
textField.text: serverUnderloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textField.text !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textField.text
}
}
checkEmptyText: true
}
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 === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onActiveFocusChanged: {
if(activeFocus) {
listView.positionViewAtEnd()
}
}
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
}
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
} }
} }
clickedFunc: function() { var headerText = qsTr("Save settings?")
forceActiveFocus() 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) { var yesButtonFunction = function() {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
transportPacketMagicHeaderTextField.textField.text, PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
responsePacketMagicHeaderTextField.textField.text, return
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
}
} }
var headerText = qsTr("Save settings?") PageController.goToPage(PageEnum.PageSetupWizardInstalling);
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") InstallController.updateContainer(AwgConfigModel.getConfig())
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)
} }
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }

View file

@ -16,179 +16,161 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.left: parent.left
anchors.right: parent.right
Column { property int selectedIndex: 0
id: content
anchors.top: parent.top enabled: ServersModel.isProcessedServerHasWriteAccess()
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() header: ColumnLayout {
ListView { width: listView.width
id: listview
property int selectedIndex: 0 BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
width: parent.width headerText: qsTr("Cloak settings")
height: listview.contentItem.height }
}
clip: true model: CloakConfigModel
reuseItems: true
model: CloakConfigModel delegate: ColumnLayout {
delegate: Item { width: listView.width
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias trafficFromField: trafficFromField property alias trafficFromField: trafficFromField
ColumnLayout { spacing: 0
id: col
anchors.top: parent.top TextFieldWithHeaderType {
anchors.left: parent.left id: trafficFromField
anchors.right: parent.right
anchors.leftMargin: 16 Layout.fillWidth: true
anchors.rightMargin: 16 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
spacing: 0 headerText: qsTr("Disguised as traffic from")
textField.text: site
BaseHeaderType { textField.onEditingFinished: {
Layout.fillWidth: true if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
headerText: qsTr("Cloak settings") var indexHttps = tmpText.indexOf("https://")
if (indexHttps === 0) {
tmpText = textField.text.substring(8)
} else {
site = textField.text
} }
}
}
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: trafficFromField id: portTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Disguised as traffic from") headerText: qsTr("Port")
textField.text: site textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { textField.onEditingFinished: {
if (textField.text !== site) { if (textField.text !== port) {
var tmpText = textField.text port = textField.text
tmpText = tmpText.toLocaleLowerCase() }
}
}
var indexHttps = tmpText.indexOf("https://") DropDownType {
if (indexHttps === 0) { id: cipherDropDown
tmpText = textField.text.substring(8)
} else {
site = textField.text
}
}
}
}
TextFieldWithHeaderType { Layout.fillWidth: true
id: portTextField Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.fillWidth: true descriptionText: qsTr("Cipher")
Layout.topMargin: 16 headerText: qsTr("Cipher")
headerText: qsTr("Port") drawerParent: root
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { listView: ListViewWithRadioButtonType {
if (textField.text !== port) { id: cipherListView
port = textField.text
}
}
}
DropDownType { rootWidth: root.width
id: cipherDropDown
Layout.fillWidth: true
Layout.topMargin: 16
descriptionText: qsTr("Cipher") model: ListModel {
headerText: qsTr("Cipher") 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" }
}
drawerParent: root clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
listView: ListViewWithRadioButtonType { Component.onCompleted: {
id: cipherListView cipherDropDown.text = cipher
rootWidth: root.width for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
model: ListModel { selectedIndex = i
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: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save")
clickedFunc: function() {
forceActiveFocus()
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())
} }
} }
} }
} }
} }
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
clickedFunc: 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())
}
}
} }
} }
} }

View file

@ -17,401 +17,380 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: OpenVpnConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("OpenVPN settings")
}
TextFieldWithHeaderType {
id: vpnAddressSubnetTextField
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
ListView { headerText: qsTr("VPN address subnet")
id: listview textField.text: subnetAddress
width: parent.width textField.onEditingFinished: {
height: listview.contentItem.height if (textField.text !== subnetAddress) {
subnetAddress = textField.text
clip: true }
interactive: false }
}
model: OpenVpnConfigModel
ParagraphTextType {
delegate: Item { Layout.fillWidth: true
implicitWidth: listview.width Layout.topMargin: 32
implicitHeight: col.implicitHeight Layout.leftMargin: 16
Layout.rightMargin: 16
property alias vpnAddressSubnetTextField: vpnAddressSubnetTextField
text: qsTr("Network protocol")
ColumnLayout { }
id: col
TransportProtoSelector {
anchors.top: parent.top id: transportProtoSelector
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.topMargin: 16
Layout.leftMargin: 16
anchors.leftMargin: 16 Layout.rightMargin: 16
anchors.rightMargin: 16 rootWidth: root.width
spacing: 0 enabled: isTransportProtoEditable
BaseHeaderType { currentIndex: {
Layout.fillWidth: true return transportProto === "tcp" ? 1 : 0
}
headerText: qsTr("OpenVPN settings")
} onCurrentIndexChanged: {
if (transportProto === "tcp" && currentIndex === 0) {
TextFieldWithHeaderType { transportProto = "udp"
id: vpnAddressSubnetTextField } else if (transportProto === "udp" && currentIndex === 1) {
transportProto = "tcp"
Layout.fillWidth: true }
Layout.topMargin: 32 }
}
headerText: qsTr("VPN address subnet")
textField.text: subnetAddress TextFieldWithHeaderType {
id: portTextField
parentFlickable: fl
Layout.fillWidth: true
textField.onEditingFinished: { Layout.topMargin: 40
if (textField.text !== subnetAddress) { Layout.leftMargin: 16
subnetAddress = textField.text Layout.rightMargin: 16
}
} enabled: isPortEditable
}
headerText: qsTr("Port")
ParagraphTextType { textField.text: port
Layout.fillWidth: true textField.maximumLength: 5
Layout.topMargin: 32 textField.validator: IntValidator { bottom: 1; top: 65535 }
text: qsTr("Network protocol") textField.onEditingFinished: {
} if (textField.text !== port) {
port = textField.text
TransportProtoSelector { }
id: transportProtoSelector }
Layout.fillWidth: true }
Layout.topMargin: 16
rootWidth: root.width SwitcherType {
id: autoNegotiateEncryprionSwitcher
enabled: isTransportProtoEditable
Layout.fillWidth: true
currentIndex: { Layout.topMargin: 24
return transportProto === "tcp" ? 1 : 0 Layout.leftMargin: 16
} Layout.rightMargin: 16
onCurrentIndexChanged: { text: qsTr("Auto-negotiate encryption")
if (transportProto === "tcp" && currentIndex === 0) { checked: autoNegotiateEncryprion
transportProto = "udp"
} else if (transportProto === "udp" && currentIndex === 1) { onCheckedChanged: {
transportProto = "tcp" if (checked !== autoNegotiateEncryprion) {
} autoNegotiateEncryprion = checked
} }
} }
}
TextFieldWithHeaderType {
id: portTextField DropDownType {
id: hashDropDown
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 20
parentFlickable: fl Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: isPortEditable
enabled: !autoNegotiateEncryprionSwitcher.checked
headerText: qsTr("Port")
textField.text: port descriptionText: qsTr("Hash")
textField.maximumLength: 5 headerText: qsTr("Hash")
textField.validator: IntValidator { bottom: 1; top: 65535 }
drawerParent: root
textField.onEditingFinished: {
if (textField.text !== port) { listView: ListViewWithRadioButtonType {
port = textField.text id: hashListView
}
} rootWidth: root.width
}
model: ListModel {
SwitcherType { ListElement { name : qsTr("SHA512") }
id: autoNegotiateEncryprionSwitcher ListElement { name : qsTr("SHA384") }
ListElement { name : qsTr("SHA256") }
Layout.fillWidth: true ListElement { name : qsTr("SHA3-512") }
Layout.topMargin: 24 ListElement { name : qsTr("SHA3-384") }
parentFlickable: fl ListElement { name : qsTr("SHA3-256") }
ListElement { name : qsTr("whirlpool") }
text: qsTr("Auto-negotiate encryption") ListElement { name : qsTr("BLAKE2b512") }
checked: autoNegotiateEncryprion ListElement { name : qsTr("BLAKE2s256") }
ListElement { name : qsTr("SHA1") }
onCheckedChanged: { }
if (checked !== autoNegotiateEncryprion) {
autoNegotiateEncryprion = checked clickedFunction: function() {
} hashDropDown.text = selectedText
} hash = hashDropDown.text
} hashDropDown.closeTriggered()
}
DropDownType {
id: hashDropDown Component.onCompleted: {
Layout.fillWidth: true hashDropDown.text = hash
Layout.topMargin: 20
for (var i = 0; i < hashListView.model.count; i++) {
enabled: !autoNegotiateEncryprionSwitcher.checked if (hashListView.model.get(i).name === hashDropDown.text) {
currentIndex = i
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: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save")
parentFlickable: fl
clickedFunc: function() {
forceActiveFocus()
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())
} }
} }
} }
} }
} }
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")
onCheckedChanged: {
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")
onCheckedChanged: {
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: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
clickedFunc: 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())
}
}
} }
} }
} }

View file

@ -19,164 +19,152 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: header id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
} listView.positionViewAtBeginning()
}
BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: header.bottom
anchors.left: parent.left anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
contentHeight: content.height anchors.left: parent.left
Column { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top BaseHeaderType {
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.leftMargin: 16
anchors.topMargin: 32 Layout.rightMargin: 16
Layout.bottomMargin: 16
ListView { headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
id: listView }
width: parent.width }
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel
activeFocusOnTab: true model: ProtocolsModel
focus: true
delegate: Item { delegate: ColumnLayout {
implicitWidth: parent.width width: listView.width
implicitHeight: delegateContent.implicitHeight
property alias focusItem: button LabelWithButtonType {
id: button
ColumnLayout { Layout.fillWidth: true
id: delegateContent Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.fill: parent text: qsTr("Show connection options")
LabelWithButtonType { clickedFunction: function() {
id: button configContentDrawer.openTriggered()
}
Layout.fillWidth: true MouseArea {
anchors.fill: button
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
text: qsTr("Show connection options") DividerType {}
clickedFunction: function() { DrawerType2 {
configContentDrawer.openTriggered() id: configContentDrawer
}
MouseArea { expandedHeight: root.height * 0.9
anchors.fill: button
cursorShape: Qt.PointingHandCursor parent: root
enabled: false 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 { delegate: ColumnLayout {
id: configContentDrawer width: drawerListView.width
expandedHeight: root.height * 0.9 TextArea {
id: configText
parent: root Layout.fillWidth: true
anchors.fill: parent Layout.topMargin: 16
expandedStateContent: Item { padding: 0
implicitHeight: configContentDrawer.expandedHeight height: 24
BackButtonType { color: AmneziaStyle.color.paleGray
id: backButton1 selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
anchors.top: parent.top font.pixelSize: 16
anchors.left: parent.left font.weight: Font.Medium
anchors.right: parent.right font.family: "PT Root UI VF"
anchors.topMargin: 16
backButtonFunction: function() { text: rawConfig
configContentDrawer.closeTriggered()
}
}
FlickableType { wrapMode: Text.Wrap
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
ColumnLayout { background: Rectangle {
id: configContent color: AmneziaStyle.color.transparent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Connection options %1").arg(protocolName)
}
TextArea {
id: configText
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
padding: 0
leftPadding: 0
height: 24
color: AmneziaStyle.color.paleGray
selectionColor: AmneziaStyle.color.richBrown
selectedTextColor: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: Font.Medium
font.family: "PT Root UI VF"
text: rawConfig
wrapMode: Text.Wrap
background: Rectangle {
color: AmneziaStyle.color.transparent
}
}
}
} }
} }
} }
} }
} }
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: removeButton id: removeButton
@ -198,11 +186,7 @@ PageType {
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }

View file

@ -16,164 +16,138 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: ShadowSocksConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { spacing: 0
id: listview
width: parent.width BaseHeaderType {
height: listview.contentItem.height Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
clip: true headerText: qsTr("Shadowsocks settings")
interactive: false }
model: ShadowSocksConfigModel TextFieldWithHeaderType {
id: portTextField
delegate: Item { Layout.fillWidth: true
implicitWidth: listview.width Layout.topMargin: 40
implicitHeight: col.implicitHeight Layout.leftMargin: 16
Layout.rightMargin: 16
property var focusItemId: portTextField.enabled ? enabled: isPortEditable
portTextField :
cipherDropDown.enabled ?
cipherDropDown :
saveRestartButton
ColumnLayout { headerText: qsTr("Port")
id: col textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
anchors.top: parent.top textField.onEditingFinished: {
anchors.left: parent.left if (textField.text !== port) {
anchors.right: parent.right port = textField.text
}
}
}
anchors.leftMargin: 16 DropDownType {
anchors.rightMargin: 16 id: cipherDropDown
spacing: 0 Layout.fillWidth: true
Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
BaseHeaderType { enabled: isCipherEditable
Layout.fillWidth: true
headerText: qsTr("Shadowsocks settings") descriptionText: qsTr("Cipher")
} headerText: qsTr("Cipher")
TextFieldWithHeaderType { drawerParent: root
id: portTextField
Layout.fillWidth: true listView: ListViewWithRadioButtonType {
Layout.topMargin: 40
enabled: isPortEditable id: cipherListView
headerText: qsTr("Port") rootWidth: root.width
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: { model: ListModel {
if (textField.text !== port) { ListElement { name : "chacha20-ietf-poly1305" }
port = textField.text ListElement { name : "xchacha20-ietf-poly1305" }
} ListElement { name : "aes-256-gcm" }
} ListElement { name : "aes-192-gcm" }
} ListElement { name : "aes-128-gcm" }
}
DropDownType { clickedFunction: function() {
id: cipherDropDown cipherDropDown.text = selectedText
Layout.fillWidth: true cipher = cipherDropDown.text
Layout.topMargin: 20 cipherDropDown.closeTriggered()
}
enabled: isCipherEditable Component.onCompleted: {
cipherDropDown.text = cipher
descriptionText: qsTr("Cipher") for (var i = 0; i < cipherListView.model.count; i++) {
headerText: qsTr("Cipher") if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
drawerParent: root
listView: ListViewWithRadioButtonType {
id: cipherListView
rootWidth: root.width
model: ListModel {
ListElement { name : "chacha20-ietf-poly1305" }
ListElement { name : "xchacha20-ietf-poly1305" }
ListElement { name : "aes-256-gcm" }
ListElement { name : "aes-192-gcm" }
ListElement { name : "aes-128-gcm" }
}
clickedFunction: function() {
cipherDropDown.text = selectedText
cipher = cipherDropDown.text
cipherDropDown.closeTriggered()
}
Component.onCompleted: {
cipherDropDown.text = cipher
for (var i = 0; i < cipherListView.model.count; i++) {
if (cipherListView.model.get(i).name === cipherDropDown.text) {
currentIndex = i
}
}
}
}
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: isPortEditable | isCipherEditable
text: qsTr("Save")
clickedFunc: function() {
forceActiveFocus()
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())
} }
} }
} }
} }
} }
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: isPortEditable | isCipherEditable
text: qsTr("Save")
clickedFunc: 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())
}
}
} }
} }
} }

View file

@ -16,160 +16,124 @@ import "../Components"
PageType { PageType {
id: root id: root
Item { BackButtonType {
id: focusItem id: backButton
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButtonLayout
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
KeyNavigation.tab: listview.currentItem.mtuTextField.textField listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.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 { model: WireGuardConfigModel
id: content
anchors.top: parent.top delegate: ColumnLayout {
anchors.left: parent.left width: listView.width
anchors.right: parent.right
ListView { property alias mtuTextField: mtuTextField
id: listview property bool isSaveButtonEnabled: mtuTextField.errorText === ""
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
model: WireGuardConfigModel headerText: qsTr("WG settings")
}
delegate: Item { TextFieldWithHeaderType {
id: delegateItem id: mtuTextField
implicitWidth: listview.width Layout.fillWidth: true
implicitHeight: col.implicitHeight Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
property alias mtuTextField: mtuTextField headerText: qsTr("MTU")
property bool isSaveButtonEnabled: mtuTextField.errorText === "" textField.text: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
ColumnLayout { textField.onEditingFinished: {
id: col if (textField.text !== clientMtu) {
clientMtu = textField.text
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
}
}
} }
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 { footer: ColumnLayout {
id: saveButton width: listView.width
anchors.right: root.right BasicButtonType {
anchors.left: root.left id: saveButton
anchors.bottom: root.bottom
anchors.topMargin: 24 Layout.fillWidth: true
anchors.bottomMargin: 24 Layout.topMargin: 24
anchors.rightMargin: 16 Layout.bottomMargin: 24
anchors.leftMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16
enabled: listview.currentItem.isSaveButtonEnabled enabled: listView.currentItem.isSaveButtonEnabled
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() var headerText = qsTr("Save settings?")
var headerText = qsTr("Save settings?") var descriptionText = qsTr("Only the settings for this device will be changed")
var descriptionText = qsTr("Only the settings for this device will be changed") var yesButtonText = qsTr("Continue")
var yesButtonText = qsTr("Continue") var noButtonText = qsTr("Cancel")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return return
} }
PageController.goToPage(PageEnum.PageSetupWizardInstalling); PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig()) InstallController.updateContainer(WireGuardConfigModel.getConfig())
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) { showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
saveButton.forceActiveFocus()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }

View file

@ -16,153 +16,134 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: WireGuardConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
id: listview
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
model: WireGuardConfigModel headerText: qsTr("WG settings")
}
delegate: Item { TextFieldWithHeaderType {
id: delegateItem id: vpnAddressSubnetTextField
property alias focusItemId: vpnAddressSubnetTextField Layout.fillWidth: true
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() Layout.topMargin: 40
Layout.leftMargin: 16
Layout.rightMargin: 16
implicitWidth: listview.width enabled: delegateItem.isEnabled
implicitHeight: col.implicitHeight
ColumnLayout { headerText: qsTr("VPN address subnet")
id: col textField.text: subnetAddress
anchors.top: parent.top textField.onEditingFinished: {
anchors.left: parent.left if (textField.text !== subnetAddress) {
anchors.right: parent.right subnetAddress = textField.text
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()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
} }
} }
checkEmptyText: true
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textField.text !== port) {
port = textField.text
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
enabled: portTextField.errorText === "" &&
vpnAddressSubnetTextField.errorText === ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
} }
} }
} }

View file

@ -17,141 +17,112 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.left: parent.left
anchors.right: parent.right
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content model: XrayConfigModel
anchors.top: parent.top delegate: ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() width: listView.width
ListView { property alias focusItemId: textFieldWithHeaderType.textField
id: listview
width: parent.width spacing: 0
height: listview.contentItem.height
clip: true BaseHeaderType {
interactive: false Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("XRay settings")
}
model: XrayConfigModel TextFieldWithHeaderType {
id: textFieldWithHeaderType
delegate: Item { Layout.fillWidth: true
property alias focusItemId: textFieldWithHeaderType.textField Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
implicitWidth: listview.width headerText: qsTr("Disguised as traffic from")
implicitHeight: col.implicitHeight textField.text: site
ColumnLayout { textField.onEditingFinished: {
id: col if (textField.text !== site) {
var tmpText = textField.text
tmpText = tmpText.toLocaleLowerCase()
anchors.top: parent.top if (tmpText.startsWith("https://")) {
anchors.left: parent.left tmpText = textField.text.substring(8)
anchors.right: parent.right site = tmpText
} else {
anchors.leftMargin: 16 site = textField.text
anchors.rightMargin: 16
spacing: 0
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("XRay settings")
}
TextFieldWithHeaderType {
id: textFieldWithHeaderType
Layout.fillWidth: true
Layout.topMargin: 32
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
}
}
}
}
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
text: qsTr("Save")
onClicked: {
forceActiveFocus()
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()
}
Keys.onEnterPressed: basicButton.clicked()
Keys.onReturnPressed: basicButton.clicked()
} }
} }
} }
} }
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
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Save")
onClicked: {
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())
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
} }
} }
} }

View file

@ -16,50 +16,47 @@ import "../Components"
PageType { PageType {
id: root id: root
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 24
headerText: "AmneziaDNS" headerText: "AmneziaDNS"
descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") + descriptionText: qsTr("A DNS service is installed on your server, and it is only accessible via VPN.\n") +
qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.") qsTr("The DNS address is the same as the address of your server. You can configure DNS in the settings, under the connections tab.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: removeButton Layout.fillWidth: true
Layout.leftMargin: 16
Layout.topMargin: 24 Layout.rightMargin: 16
width: parent.width
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
@ -71,19 +68,14 @@ PageType {
var yesButtonFunction = function() { var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& SettingsController.isAmneziaDnsEnabled()) { && SettingsController.isAmneziaDnsEnabled()) {
PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server")) PageController.showNotificationMessage(qsTr("Cannot remove AmneziaDNS from running server"))
} else } else {
{
PageController.goToPage(PageEnum.PageDeinstalling) PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer() InstallController.removeProcessedContainer()
} }
} }
var noButtonFunction = function() { var noButtonFunction = function() {}
if (!GC.isMobile()) {
removeButton.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }

View file

@ -24,258 +24,215 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
Column { enabled: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top model: SftpConfigModel
anchors.left: parent.left
anchors.right: parent.right
enabled: ServersModel.isProcessedServerHasWriteAccess() delegate: ColumnLayout {
width: listView.width
ListView { spacing: 0
id: listview
width: parent.width BaseHeaderType {
height: listview.contentItem.height Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
clip: true headerText: qsTr("SFTP settings")
interactive: false }
model: SftpConfigModel LabelWithButtonType {
id: hostLabel
onFocusChanged: { Layout.fillWidth: true
if (focus) { Layout.topMargin: 32
listview.currentItem.listViewFocusItem.forceActiveFocus() 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 { MouseArea {
implicitWidth: listview.width anchors.fill: parent
implicitHeight: col.implicitHeight acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
property alias listViewFocusItem: hostLabel.rightButton BasicButtonType {
id: detailedInstructionsButton
ColumnLayout { Layout.topMargin: 16
id: col Layout.bottomMargin: 16
Layout.leftMargin: 8
implicitHeight: 32
anchors.top: parent.top defaultColor: AmneziaStyle.color.transparent
anchors.left: parent.left hoveredColor: AmneziaStyle.color.translucentWhite
anchors.right: parent.right pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
spacing: 0 text: qsTr("Detailed instructions")
BaseHeaderType { clickedFunc: function() {
Layout.fillWidth: true // Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
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")
}
}
}
} }
} }
} }

View file

@ -25,327 +25,290 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { onFocusChanged: {
id: backButton if (this.activeFocus) {
listView.positionViewAtBeginning()
}
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: listview.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ListView { model: Socks5ProxyConfigModel
id: listview
width: parent.width delegate: ColumnLayout {
height: listview.contentItem.height width: listView.width
clip: true spacing: 0
interactive: false
model: Socks5ProxyConfigModel BaseHeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
onFocusChanged: { headerText: qsTr("SOCKS5 settings")
if (focus) { }
listview.currentItem.focusItemId.forceActiveFocus()
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
} }
} }
delegate: Item { LabelWithButtonType {
implicitWidth: listview.width Layout.fillWidth: true
implicitHeight: content.implicitHeight Layout.rightMargin: 16
Layout.bottomMargin: 16
property alias focusItemId: hostLabel.rightButton text: qsTr("Port")
descriptionText: port
ColumnLayout { descriptionOnTop: true
id: content
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("User name")
descriptionText: username
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
DrawerType2 {
id: changeSettingsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.9
expandedStateContent: ColumnLayout {
property string tempPort: port
property string tempUsername: username
property string tempPassword: password
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0 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 { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("SOCKS5 settings") headerText: qsTr("SOCKS5 settings")
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: hostLabel id: portTextField
Layout.fillWidth: true 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") textField.onEditingFinished: {
descriptionText: ServersModel.getProcessedServerData("hostName") textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== port) {
descriptionOnTop: true port = textField.text
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
} }
} }
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: portLabel id: usernameTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("Port") headerText: qsTr("Username")
descriptionText: port textField.placeholderText: "username"
textField.text: username
textField.maximumLength: 32
descriptionOnTop: true textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
parentFlickable: fl if (textField.text !== username) {
username = textField.text
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
} }
} }
} }
LabelWithButtonType { TextFieldWithHeaderType {
id: usernameLabel id: passwordTextField
property bool hidePassword: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
text: qsTr("User name") headerText: qsTr("Password")
descriptionText: username 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 clickedFunc: function() {
hidePassword = !hidePassword
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
}
LabelWithButtonType { textField.onFocusChanged: {
id: passwordLabel textField.text = textField.text.replace(/^\s+|\s+$/g, '')
Layout.fillWidth: true if (textField.text !== password) {
password = textField.text
text: qsTr("Password")
descriptionText: password
descriptionOnTop: true
parentFlickable: fl
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray
buttonImageSource: hideDescription ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
DrawerType2 {
id: changeSettingsDrawer
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.9
expandedStateContent: ColumnLayout {
property string tempPort: port
property string tempUsername: username
property string tempPassword: password
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
Connections {
target: changeSettingsDrawer
function onOpened() {
tempPort = port
tempUsername = username
tempPassword = password
}
function onClosed() {
port = tempPort
username = tempUsername
password = tempPassword
portTextField.textField.text = port
usernameTextField.textField.text = username
passwordTextField.textField.text = password
}
}
BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("SOCKS5 settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
headerText: qsTr("Port")
textField.text: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== port) {
port = textField.text
}
}
}
TextFieldWithHeaderType {
id: usernameTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Username")
textField.placeholderText: "username"
textField.text: username
textField.maximumLength: 32
textField.onEditingFinished: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== username) {
username = textField.text
}
}
}
TextFieldWithHeaderType {
id: passwordTextField
property bool hidePassword: true
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("Password")
textField.placeholderText: "password"
textField.text: password
textField.maximumLength: 32
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
buttonImageSource: textField.text !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: ""
clickedFunc: function() {
hidePassword = !hidePassword
}
textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
if (textField.text !== password) {
password = textField.text
}
}
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Change connection settings")
clickedFunc: function() {
forceActiveFocus()
if (!portTextField.textField.acceptableInput) {
portTextField.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
if (usernameTextField.textField.text && passwordTextField.textField.text === "") {
passwordTextField.errorText = qsTr("Password cannot be empty")
return
} else if (usernameTextField.textField.text === "" && passwordTextField.textField.text) {
usernameTextField.errorText = qsTr("Username cannot be empty")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(Socks5ProxyConfigModel.getConfig())
tempPort = portTextField.textField.text
tempUsername = usernameTextField.textField.text
tempPassword = passwordTextField.textField.text
changeSettingsDrawer.closeTriggered()
}
} }
} }
} }
BasicButtonType { BasicButtonType {
id: changeSettingsButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() if (!portTextField.textField.acceptableInput) {
changeSettingsDrawer.openTriggered() 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()
}
}
} }
} }
} }

View file

@ -25,34 +25,25 @@ PageType {
} }
} }
ColumnLayout { BackButtonType {
id: backButtonLayout id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType {
id: backButton
}
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButtonLayout.bottom
anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -61,11 +52,19 @@ PageType {
headerText: qsTr("Tor website settings") headerText: qsTr("Tor website settings")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: websiteName id: websiteName
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.bottomMargin: 24
text: qsTr("Website address") text: qsTr("Website address")
descriptionText: { descriptionText: {
@ -83,15 +82,16 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
} }
} }
}
footer: ColumnLayout {
width: listView.width
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 40 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16

View file

@ -1,156 +1,167 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
FlickableType { ListViewType {
id: fl id: listView
anchors.top: parent.top
anchors.bottom: parent.bottom anchors.fill: parent
contentHeight: content.height
header: ColumnLayout {
ColumnLayout { width: listView.width
id: content
BaseHeaderType {
anchors.top: parent.top id: header
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.topMargin: 24
Layout.bottomMargin: 16
spacing: 0 Layout.rightMargin: 16
Layout.leftMargin: 16
BaseHeaderType {
id: header headerText: qsTr("Settings")
Layout.fillWidth: true }
Layout.topMargin: 24 }
Layout.rightMargin: 16
Layout.leftMargin: 16 model: settingsEntries
headerText: qsTr("Settings") delegate: ColumnLayout {
} width: listView.width
LabelWithButtonType { spacing: 0
id: account
Layout.fillWidth: true LabelWithButtonType {
Layout.topMargin: 16 Layout.fillWidth: true
Layout.leftMargin: 16
text: qsTr("Servers") Layout.rightMargin: 16
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/server.svg" visible: isVisible
clickedFunction: function() { text: title
PageController.goToPage(PageEnum.PageSettingsServersList) rightImageSource: "qrc:/images/controls/chevron-right.svg"
} leftImageSource: leftImagePath
}
clickedFunction: clickedHandler
DividerType {} }
LabelWithButtonType { DividerType {
id: connection visible: isVisible
Layout.fillWidth: true }
}
text: qsTr("Connection")
rightImageSource: "qrc:/images/controls/chevron-right.svg" footer: ColumnLayout {
leftImageSource: "qrc:/images/controls/radio.svg" width: listView.width
clickedFunction: function() { LabelWithButtonType {
PageController.goToPage(PageEnum.PageSettingsConnection) id: close
}
} visible: GC.isDesktop()
Layout.fillWidth: true
DividerType {} Layout.leftMargin: 16
Layout.rightMargin: 16
LabelWithButtonType {
id: application text: qsTr("Close application")
Layout.fillWidth: true leftImageSource: "qrc:/images/controls/x-circle.svg"
isLeftImageHoverEnabled: false
text: qsTr("Application")
rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() {
leftImageSource: "qrc:/images/controls/app.svg" PageController.closeApplication()
}
clickedFunction: function() { }
PageController.goToPage(PageEnum.PageSettingsApplication)
} DividerType {
} Layout.fillWidth: true
Layout.leftMargin: 16
DividerType {} Layout.rightMargin: 16
LabelWithButtonType { visible: GC.isDesktop()
id: backup }
Layout.fillWidth: true }
}
text: qsTr("Backup")
rightImageSource: "qrc:/images/controls/chevron-right.svg" property list<QtObject> settingsEntries: [
leftImageSource: "qrc:/images/controls/save.svg" servers,
connection,
clickedFunction: function() { application,
PageController.goToPage(PageEnum.PageSettingsBackup) backup,
} about,
} devConsole
]
DividerType {}
QtObject {
LabelWithButtonType { id: servers
id: about
Layout.fillWidth: true property string title: qsTr("Servers")
readonly property string leftImagePath: "qrc:/images/controls/server.svg"
text: qsTr("About AmneziaVPN") property bool isVisible: true
rightImageSource: "qrc:/images/controls/chevron-right.svg" readonly property var clickedHandler: function() {
leftImageSource: "qrc:/images/controls/amnezia.svg" PageController.goToPage(PageEnum.PageSettingsServersList)
}
clickedFunction: function() { }
PageController.goToPage(PageEnum.PageSettingsAbout)
} QtObject {
} id: connection
DividerType {} property string title: qsTr("Connection")
readonly property string leftImagePath: "qrc:/images/controls/radio.svg"
LabelWithButtonType { property bool isVisible: true
id: devConsole readonly property var clickedHandler: function() {
visible: SettingsController.isDevModeEnabled PageController.goToPage(PageEnum.PageSettingsConnection)
Layout.fillWidth: true }
}
text: qsTr("Dev console")
rightImageSource: "qrc:/images/controls/chevron-right.svg" QtObject {
leftImageSource: "qrc:/images/controls/bug.svg" id: application
clickedFunction: function() { property string title: qsTr("Application")
PageController.goToPage(PageEnum.PageDevMenu) readonly property string leftImagePath: "qrc:/images/controls/app.svg"
} property bool isVisible: true
} readonly property var clickedHandler: function() {
PageController.goToPage(PageEnum.PageSettingsApplication)
DividerType { }
visible: SettingsController.isDevModeEnabled }
}
QtObject {
LabelWithButtonType { id: backup
id: close
visible: GC.isDesktop() property string title: qsTr("Backup")
Layout.fillWidth: true readonly property string leftImagePath: "qrc:/images/controls/save.svg"
Layout.preferredHeight: about.height property bool isVisible: true
readonly property var clickedHandler: function() {
text: qsTr("Close application") PageController.goToPage(PageEnum.PageSettingsBackup)
leftImageSource: "qrc:/images/controls/x-circle.svg" }
isLeftImageHoverEnabled: false }
clickedFunction: function() { QtObject {
PageController.closeApplication() id: about
}
} property string title: qsTr("About AmneziaVPN")
readonly property string leftImagePath: "qrc:/images/controls/amnezia.svg"
DividerType { property bool isVisible: true
visible: GC.isDesktop() 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)
}
}
}

View file

@ -29,58 +29,7 @@ PageType {
} }
} }
QtObject { ListViewType {
id: telegramGroup
readonly property string title: qsTr("Telegram group")
readonly property string description: qsTr("To discuss features")
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
}
QtObject {
id: mail
readonly property string title: qsTr("support@amnezia.org")
readonly property string description: qsTr("For reviews and bug reports")
readonly property string imageSource: "qrc:/images/controls/mail.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
}
QtObject {
id: github
readonly property string title: qsTr("GitHub")
readonly property string description: qsTr("Discover the source code")
readonly property string imageSource: "qrc:/images/controls/github.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
QtObject {
id: website
readonly property string title: qsTr("Website")
readonly property string description: qsTr("Visit official website")
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
readonly property var handler: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
property list<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
ListView {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@ -88,38 +37,6 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {}
model: contacts
clip: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -170,6 +87,8 @@ PageType {
} }
} }
model: contacts
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
@ -257,4 +176,55 @@ PageType {
} }
} }
} }
QtObject {
id: telegramGroup
readonly property string title: qsTr("Telegram group")
readonly property string description: qsTr("To discuss features")
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
}
QtObject {
id: mail
readonly property string title: qsTr("support@amnezia.org")
readonly property string description: qsTr("For reviews and bug reports")
readonly property string imageSource: "qrc:/images/controls/mail.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
}
QtObject {
id: github
readonly property string title: qsTr("GitHub")
readonly property string description: qsTr("Discover the source code")
readonly property string imageSource: "qrc:/images/controls/github.svg"
readonly property var handler: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
QtObject {
id: website
readonly property string title: qsTr("Website")
readonly property string description: qsTr("Visit official website")
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
readonly property var handler: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
property list<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
} }

View file

@ -1,220 +1,236 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
property string configExtension: ".conf" property string configExtension: ".conf"
property string configCaption: qsTr("Save AmneziaVPN config") property string configCaption: qsTr("Save AmneziaVPN config")
ListViewType { BackButtonType {
id: listView id: backButton
anchors.fill: parent anchors.top: parent.top
anchors.topMargin: 20 anchors.left: parent.left
anchors.bottomMargin: 24 anchors.right: parent.right
anchors.topMargin: 20
model: ApiCountryModel }
header: ColumnLayout { ListViewType {
width: listView.width id: listView
BackButtonType { anchors.top: backButton.bottom
id: backButton anchors.bottom: parent.bottom
} anchors.right: parent.right
anchors.left: parent.left
BaseHeaderType {
id: header model: ApiCountryModel
Layout.fillWidth: true header: ColumnLayout {
Layout.rightMargin: 16 width: listView.width
Layout.leftMargin: 16
BaseHeaderType {
headerText: qsTr("Configuration Files") id: header
descriptionText: qsTr("For router setup or the AmneziaWG app")
} Layout.fillWidth: true
} Layout.rightMargin: 16
Layout.leftMargin: 16
delegate: ColumnLayout {
width: listView.width headerText: qsTr("Configuration Files")
descriptionText: qsTr("For router setup or the AmneziaWG app")
LabelWithButtonType { }
Layout.fillWidth: true }
Layout.topMargin: 6
delegate: ColumnLayout {
text: countryName width: listView.width
descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
descriptionColor: AmneziaStyle.color.vibrantRed LabelWithButtonType {
Layout.fillWidth: true
leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" Layout.topMargin: 6
rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
text: countryName
clickedFunction: function() { descriptionText: isWorkerExpired ? qsTr("The configuration needs to be reissued") : ""
if (isIssued) { descriptionColor: AmneziaStyle.color.vibrantRed
moreOptionsDrawer.countryName = countryName
moreOptionsDrawer.countryCode = countryCode leftImageSource: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
moreOptionsDrawer.openTriggered() rightImageSource: isIssued ? "qrc:/images/controls/more-vertical.svg" : "qrc:/images/controls/download.svg"
} else {
issueConfig(countryCode) clickedFunction: function() {
} if (isIssued) {
} moreOptionsDrawer.countryName = countryName
} moreOptionsDrawer.countryCode = countryCode
moreOptionsDrawer.openTriggered()
DividerType {} } else {
} issueConfig(countryCode)
} }
}
DrawerType2 { }
id: moreOptionsDrawer
DividerType {}
property string countryName }
property string countryCode }
anchors.fill: parent DrawerType2 {
expandedHeight: parent.height * 0.4375 id: moreOptionsDrawer
expandedStateContent: Item { property string countryName
implicitHeight: moreOptionsDrawer.expandedHeight property string countryCode
BackButtonType { anchors.fill: parent
id: moreOptionsDrawerBackButton expandedHeight: parent.height * 0.4375
anchors.top: parent.top expandedStateContent: Item {
anchors.left: parent.left implicitHeight: moreOptionsDrawer.expandedHeight
anchors.right: parent.right
anchors.topMargin: 16 BackButtonType {
id: moreOptionsDrawerBackButton
backButtonFunction: function() {
moreOptionsDrawer.closeTriggered() anchors.top: parent.top
} anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
FlickableType {
anchors.top: moreOptionsDrawerBackButton.bottom backButtonFunction: function() {
anchors.left: parent.left moreOptionsDrawer.closeTriggered()
anchors.right: parent.right }
anchors.bottom: parent.bottom }
contentHeight: moreOptionsDrawerContent.height ListViewType {
id: drawerListView
ColumnLayout {
id: moreOptionsDrawerContent anchors.top: moreOptionsDrawerBackButton.bottom
anchors.bottom: parent.bottom
anchors.top: parent.top anchors.left: parent.left
anchors.left: parent.left anchors.right: parent.right
anchors.right: parent.right
header: ColumnLayout {
Header2Type { width: drawerListView.width
Layout.fillWidth: true
Layout.margins: 16 Header2Type {
Layout.fillWidth: true
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file") Layout.margins: 16
}
headerText: moreOptionsDrawer.countryName + qsTr(" configuration file")
LabelWithButtonType { }
Layout.fillWidth: true }
text: qsTr("Generate a new configuration file") model: 1 // fake model to force the ListView to be created without a model
descriptionText: qsTr("The previously created one will stop working")
delegate: ColumnLayout {
clickedFunction: function() { width: drawerListView.width
showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
} LabelWithButtonType {
} Layout.fillWidth: true
Layout.leftMargin: 16
DividerType {} Layout.rightMargin: 16
LabelWithButtonType { text: qsTr("Generate a new configuration file")
Layout.fillWidth: true descriptionText: qsTr("The previously created one will stop working")
text: qsTr("Revoke the current configuration file")
clickedFunction: function() {
clickedFunction: function() { showQuestion(true, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName) }
} }
}
DividerType {}
DividerType {} }
}
} footer: ColumnLayout {
} width: drawerListView.width
}
LabelWithButtonType {
function issueConfig(countryCode) { Layout.fillWidth: true
var fileName = "" Layout.leftMargin: 16
if (GC.isMobile()) { Layout.rightMargin: 16
fileName = countryCode + configExtension
} else { text: qsTr("Revoke the current configuration file")
fileName = SystemController.getFileName(configCaption,
qsTr("Config files (*" + configExtension + ")"), clickedFunction: function() {
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode, showQuestion(false, moreOptionsDrawer.countryCode, moreOptionsDrawer.countryName)
true, }
configExtension) }
}
if (fileName !== "") { DividerType {}
PageController.showBusyIndicator(true) }
let result = ApiConfigsController.exportNativeConfig(countryCode, fileName) }
if (result) { }
ApiSettingsController.getAccountInfo(true) }
}
function issueConfig(countryCode) {
PageController.showBusyIndicator(false) var fileName = ""
if (result) { if (GC.isMobile()) {
PageController.showNotificationMessage(qsTr("Config file saved")) fileName = countryCode + configExtension
} } else {
} fileName = SystemController.getFileName(configCaption,
} qsTr("Config files (*" + configExtension + ")"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/" + countryCode,
function revokeConfig(countryCode) { true,
PageController.showBusyIndicator(true) configExtension)
let result = ApiConfigsController.revokeNativeConfig(countryCode) }
if (result) { if (fileName !== "") {
ApiSettingsController.getAccountInfo(true) PageController.showBusyIndicator(true)
} let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
PageController.showBusyIndicator(false) if (result) {
ApiSettingsController.getAccountInfo(true)
if (result) { }
PageController.showNotificationMessage(qsTr("The config has been revoked"))
} PageController.showBusyIndicator(false)
} if (result) {
PageController.showNotificationMessage(qsTr("Config file saved"))
function showQuestion(isConfigIssue, countryCode, countryName) { }
var headerText }
if (isConfigIssue) { }
headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} else { function revokeConfig(countryCode) {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName) PageController.showBusyIndicator(true)
} let result = ApiConfigsController.revokeNativeConfig(countryCode)
if (result) {
var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it") ApiSettingsController.getAccountInfo(true)
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue") }
var noButtonText = qsTr("Cancel") PageController.showBusyIndicator(false)
var yesButtonFunction = function() { if (result) {
if (isConfigIssue) { PageController.showNotificationMessage(qsTr("The config has been revoked"))
issueConfig(countryCode) }
} else { }
revokeConfig(countryCode)
} function showQuestion(isConfigIssue, countryCode, countryName) {
moreOptionsDrawer.closeTriggered() var headerText
} if (isConfigIssue) {
var noButtonFunction = function() { headerText = qsTr("Generate a new %1 configuration file?").arg(countryName)
} } else {
headerText = qsTr("Revoke the current %1 configuration file?").arg(countryName)
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) }
}
} var descriptionText = qsTr("Your previous configuration file will no longer work, and it will not be possible to connect using it")
var yesButtonText = isConfigIssue ? qsTr("Download") : qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (isConfigIssue) {
issueConfig(countryCode)
} else {
revokeConfig(countryCode)
}
moreOptionsDrawer.closeTriggered()
}
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}

View file

@ -50,6 +50,7 @@ PageType {
readonly property string name: qsTr("Only the apps from the list should have access via VPN") readonly property string name: qsTr("Only the apps from the list should have access via VPN")
readonly property int type: routeMode.onlyForwardApps readonly property int type: routeMode.onlyForwardApps
} }
QtObject { QtObject {
id: allExceptApps id: allExceptApps
@ -146,77 +147,56 @@ PageType {
} }
} }
FlickableType { ListViewType {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.bottom: addAppButton.top
contentHeight: col.implicitHeight + addAppButton.implicitHeight + addAppButton.anchors.bottomMargin + addAppButton.anchors.topMargin anchors.left: parent.left
anchors.right: parent.right
enabled: root.pageEnabled model: SortFilterProxyModel {
id: proxyAppSplitTunnelingModel
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter {
roleName: "appPath"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
sorters: [
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
Column { delegate: ColumnLayout {
id: col width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { LabelWithButtonType {
id: apps Layout.fillWidth: true
width: parent.width
height: apps.contentItem.height
model: SortFilterProxyModel { Layout.leftMargin: 16
id: proxyAppSplitTunnelingModel Layout.rightMargin: 16
sourceModel: AppSplitTunnelingModel
filters: RegExpFilter { text: appPath
roleName: "appPath" rightImageSource: "qrc:/images/controls/trash.svg"
pattern: ".*" + searchField.textField.text + ".*" rightImageColor: AmneziaStyle.color.paleGray
caseSensitivity: Qt.CaseInsensitive
clickedFunction: function() {
var headerText = qsTr("Remove ") + appPath + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
} }
sorters: [ var noButtonFunction = function() {
RoleSorter { roleName: "appPath"; sortOrder: Qt.AscendingOrder }
]
}
clip: true
interactive: false
delegate: Item {
implicitWidth: apps.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType {
Layout.fillWidth: true
text: appPath
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + appPath + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
AppSplitTunnelingController.removeApp(proxyAppSplitTunnelingModel.mapToSource(index))
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
DividerType {}
} }
} }

View file

@ -23,20 +23,16 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -45,9 +41,17 @@ PageType {
headerText: qsTr("Application") headerText: qsTr("Application")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
SwitcherType { SwitcherType {
id: switcher id: switcherAllowScreenshots
visible: GC.isMobile() visible: GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -61,10 +65,6 @@ PageType {
SettingsController.toggleScreenshotsEnabled(checked) SettingsController.toggleScreenshotsEnabled(checked)
} }
} }
// KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
// labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
parentFlickable: fl
} }
DividerType { DividerType {
@ -73,15 +73,15 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonNotification id: labelWithButtonNotification
visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted visible: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Enable notifications") text: qsTr("Enable notifications")
descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") descriptionText: qsTr("Enable notifications to show the VPN state in the status bar")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
SettingsController.requestNotificationPermission() SettingsController.requestNotificationPermission()
} }
@ -93,6 +93,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoStart id: switcherAutoStart
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -101,8 +102,6 @@ PageType {
text: qsTr("Auto start") text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts") descriptionText: qsTr("Launch the application every time the device is starts")
parentFlickable: fl
checked: SettingsController.isAutoStartEnabled() checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoStartEnabled()) { if (checked !== SettingsController.isAutoStartEnabled()) {
@ -117,6 +116,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherAutoConnect id: switcherAutoConnect
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -125,8 +125,6 @@ PageType {
text: qsTr("Auto connect") text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start") descriptionText: qsTr("Connect to VPN on app start")
parentFlickable: fl
checked: SettingsController.isAutoConnectEnabled() checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) { if (checked !== SettingsController.isAutoConnectEnabled()) {
@ -141,6 +139,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcherStartMinimized id: switcherStartMinimized
visible: !GC.isMobile() visible: !GC.isMobile()
Layout.fillWidth: true Layout.fillWidth: true
@ -149,8 +148,6 @@ PageType {
text: qsTr("Start minimized") text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized") descriptionText: qsTr("Launch application minimized")
parentFlickable: fl
checked: SettingsController.isStartMinimizedEnabled() checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isStartMinimizedEnabled()) { if (checked !== SettingsController.isStartMinimizedEnabled()) {
@ -162,17 +159,21 @@ PageType {
DividerType { DividerType {
visible: !GC.isMobile() visible: !GC.isMobile()
} }
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLanguage id: labelWithButtonLanguage
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Language") text: qsTr("Language")
descriptionText: LanguageModel.currentLanguageName descriptionText: LanguageModel.currentLanguageName
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
selectLanguageDrawer.openTriggered() selectLanguageDrawer.openTriggered()
} }
@ -182,14 +183,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonLogging id: labelWithButtonLogging
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Logging") text: qsTr("Logging")
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
} }
@ -199,14 +199,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: labelWithButtonReset id: labelWithButtonReset
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Reset settings and remove all data from the application") text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Reset settings and remove all data from the application?") var headerText = qsTr("Reset settings and remove all data from the application?")
var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.") var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")

View file

@ -43,49 +43,60 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Back up your configuration") headerText: qsTr("Back up your configuration")
descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.") descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
spacing: 16
WarningType { WarningType {
Layout.topMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textString: qsTr("The backup will contain your passwords and private keys for all servers added " + textString: qsTr("The backup will contain your passwords and private keys for all servers added " +
"to AmneziaVPN. Keep this information in a secure place.") "to AmneziaVPN. Keep this information in a secure place.")
iconPath: "qrc:/images/controls/alert-circle.svg" iconPath: "qrc:/images/controls/alert-circle.svg"
} }
BasicButtonType { BasicButtonType {
id: makeBackupButton id: makeBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 14 Layout.topMargin: 14
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Make a backup") text: qsTr("Make a backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@ -108,8 +119,11 @@ PageType {
BasicButtonType { BasicButtonType {
id: restoreBackupButton id: restoreBackupButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -120,8 +134,6 @@ PageType {
text: qsTr("Restore from backup") text: qsTr("Restore from backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var filePath = SystemController.getFileName(qsTr("Open backup file"), var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)")) qsTr("Backup files (*.backup)"))

View file

@ -23,18 +23,17 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { header: ColumnLayout {
id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -43,9 +42,17 @@ PageType {
headerText: qsTr("Connection") headerText: qsTr("Connection")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
SwitcherType { SwitcherType {
id: amneziaDnsSwitch id: amneziaDnsSwitch
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
@ -64,14 +71,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: dnsServersButton id: dnsServersButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("DNS servers") text: qsTr("DNS servers")
descriptionText: qsTr("When AmneziaDNS is not used or installed") descriptionText: qsTr("When AmneziaDNS is not used or installed")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsDns) PageController.goToPage(PageEnum.PageSettingsDns)
} }
@ -81,14 +87,13 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton id: splitTunnelingButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Site-based split tunneling") text: qsTr("Site-based split tunneling")
descriptionText: qsTr("Allows you to select which sites you want to access through the VPN") descriptionText: qsTr("Allows you to select which sites you want to access through the VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
} }
@ -96,8 +101,15 @@ PageType {
DividerType {} DividerType {}
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: splitTunnelingButton2 id: splitTunnelingButton2
visible: root.isAppSplitTinnelingEnabled visible: root.isAppSplitTinnelingEnabled
Layout.fillWidth: true Layout.fillWidth: true
@ -106,8 +118,6 @@ PageType {
descriptionText: qsTr("Allows you to use the VPN only for certain Apps") descriptionText: qsTr("Allows you to use the VPN only for certain Apps")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
} }

View file

@ -1,140 +1,167 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Config" import "../Config"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { BackButtonType {
id: backButton id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
onFocusChanged: {
FlickableType { if (this.activeFocus) {
id: fl listView.positionViewAtBeginning()
anchors.top: backButton.bottom }
anchors.bottom: parent.bottom }
contentHeight: content.height }
property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex) ListViewType {
id: listView
enabled: !isServerFromApi
anchors.top: backButton.bottom
Component.onCompleted: { anchors.bottom: parent.bottom
if (isServerFromApi) { anchors.right: parent.right
PageController.showNotificationMessage(qsTr("Default server does not support custom DNS")) anchors.left: parent.left
}
} property var isServerFromApi: ServersModel.isServerFromApi(ServersModel.defaultIndex)
ColumnLayout { enabled: !isServerFromApi
id: content
Component.onCompleted: {
anchors.top: parent.top if (isServerFromApi) {
anchors.left: parent.left PageController.showNotificationMessage(qsTr("Default server does not support custom DNS"))
anchors.right: parent.right }
anchors.leftMargin: 16 }
anchors.rightMargin: 16
header: ColumnLayout {
spacing: 16 width: listView.width
spacing: 16
BaseHeaderType {
Layout.fillWidth: true BaseHeaderType {
Layout.fillWidth: true
headerText: qsTr("DNS servers") Layout.leftMargin: 16
} Layout.rightMargin: 16
ParagraphTextType { headerText: qsTr("DNS servers")
Layout.fillWidth: true }
text: qsTr("If AmneziaDNS is not used or installed")
} ParagraphTextType {
Layout.fillWidth: true
TextFieldWithHeaderType { Layout.leftMargin: 16
id: primaryDns Layout.rightMargin: 16
Layout.fillWidth: true text: qsTr("If AmneziaDNS is not used or installed")
headerText: qsTr("Primary DNS") }
textField.text: SettingsController.primaryDns TextFieldWithHeaderType {
textField.validator: RegularExpressionValidator { id: primaryDns
regularExpression: InstallController.ipAddressRegExp()
} Layout.fillWidth: true
} Layout.leftMargin: 16
Layout.rightMargin: 16
TextFieldWithHeaderType {
id: secondaryDns headerText: qsTr("Primary DNS")
Layout.fillWidth: true textField.text: SettingsController.primaryDns
headerText: qsTr("Secondary DNS") textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp()
textField.text: SettingsController.secondaryDns }
textField.validator: RegularExpressionValidator { }
regularExpression: InstallController.ipAddressRegExp()
} TextFieldWithHeaderType {
} id: secondaryDns
BasicButtonType { Layout.fillWidth: true
id: restoreDefaultButton Layout.leftMargin: 16
Layout.fillWidth: true Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent headerText: qsTr("Secondary DNS")
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite textField.text: SettingsController.secondaryDns
disabledColor: AmneziaStyle.color.mutedGray textField.validator: RegularExpressionValidator {
textColor: AmneziaStyle.color.paleGray regularExpression: InstallController.ipAddressRegExp()
borderWidth: 1 }
}
text: qsTr("Restore default") }
clickedFunc: function() { model: 1 // fake model to force the ListView to be created without a model
var headerText = qsTr("Restore default DNS settings?") spacing: 16
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel") delegate: ColumnLayout {
width: listView.width
var yesButtonFunction = function() {
SettingsController.primaryDns = "1.1.1.1" BasicButtonType {
primaryDns.textField.text = SettingsController.primaryDns id: restoreDefaultButton
SettingsController.secondaryDns = "1.0.0.1"
secondaryDns.textField.text = SettingsController.secondaryDns Layout.fillWidth: true
PageController.showNotificationMessage(qsTr("Settings have been reset")) Layout.topMargin: 16
} Layout.leftMargin: 16
var noButtonFunction = function() { Layout.rightMargin: 16
}
defaultColor: AmneziaStyle.color.transparent
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) hoveredColor: AmneziaStyle.color.translucentWhite
} pressedColor: AmneziaStyle.color.sheerWhite
} disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.paleGray
BasicButtonType { borderWidth: 1
id: saveButton
text: qsTr("Restore default")
Layout.fillWidth: true
clickedFunc: function() {
text: qsTr("Save") var headerText = qsTr("Restore default DNS settings?")
var yesButtonText = qsTr("Continue")
clickedFunc: function() { var noButtonText = qsTr("Cancel")
if (primaryDns.textField.text !== SettingsController.primaryDns) {
SettingsController.primaryDns = primaryDns.textField.text var yesButtonFunction = function() {
} SettingsController.primaryDns = "1.1.1.1"
if (secondaryDns.textField.text !== SettingsController.secondaryDns) { primaryDns.textField.text = SettingsController.primaryDns
SettingsController.secondaryDns = secondaryDns.textField.text SettingsController.secondaryDns = "1.0.0.1"
} secondaryDns.textField.text = SettingsController.secondaryDns
PageController.showNotificationMessage(qsTr("Settings saved")) PageController.showNotificationMessage(qsTr("Settings have been reset"))
} }
} var noButtonFunction = function() {
} }
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
}
}
footer: ColumnLayout {
width: listView.width
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Save")
clickedFunc: function() {
if (primaryDns.textField.text !== SettingsController.primaryDns) {
SettingsController.primaryDns = primaryDns.textField.text
}
if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
SettingsController.secondaryDns = secondaryDns.textField.text
}
PageController.showNotificationMessage(qsTr("Settings saved"))
}
}
}
}
}

View file

@ -25,7 +25,7 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
@ -33,10 +33,6 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -101,8 +97,7 @@ PageType {
} }
model: logTypes model: logTypes
clip: true
reuseItems: true
snapMode: ListView.SnapOneItem snapMode: ListView.SnapOneItem
delegate: ColumnLayout { delegate: ColumnLayout {

View file

@ -18,10 +18,6 @@ PageType {
signal lastItemTabClickedSignal() signal lastItemTabClickedSignal()
onFocusChanged: content.isServerWithWriteAccess ?
labelWithButton.forceActiveFocus() :
labelWithButton3.forceActiveFocus()
Connections { Connections {
target: InstallController target: InstallController
@ -67,214 +63,192 @@ PageType {
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout { property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
id: content
anchors.top: parent.top anchors.fill: parent
anchors.left: parent.left
anchors.right: parent.right
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess() model: serverActions
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: labelWithButton
visible: content.isServerWithWriteAccess
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Check the server for previously installed Amnezia services") visible: isVisible
descriptionText: qsTr("Add them to the application if they were not displayed")
text: title
descriptionText: description
textColor: tColor
clickedFunction: function() { clickedFunction: function() {
PageController.showBusyIndicator(true) clickedHandler()
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
} }
} }
DividerType { DividerType {
visible: content.isServerWithWriteAccess visible: isVisible
}
LabelWithButtonType {
id: labelWithButton2
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Reboot server")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to reboot the server?")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton2.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {
visible: content.isServerWithWriteAccess
}
LabelWithButtonType {
id: labelWithButton3
Layout.fillWidth: true
text: qsTr("Remove server from application")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to remove the server from application?")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton3.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {}
LabelWithButtonType {
id: labelWithButton4
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Clear server from Amnezia software")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers()
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton4.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {
visible: content.isServerWithWriteAccess
}
LabelWithButtonType {
id: labelWithButton5
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
Layout.fillWidth: true
text: qsTr("Reset API config")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
}
LabelWithButtonType {
id: labelWithButton6
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
Layout.fillWidth: true
text: qsTr("Switch to the new Amnezia Premium subscription")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer()
}
}
DividerType {
visible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
} }
} }
} }
property list<QtObject> serverActions: [
check,
reboot,
remove,
clear,
reset,
switch_to_premium,
]
QtObject {
id: check
property bool isVisible: true
readonly property string title: qsTr("Check the server for previously installed Amnezia services")
readonly property string description: qsTr("Add them to the application if they were not displayed")
readonly property var tColor: AmneziaStyle.color.paleGray
readonly property var clickedHandler: function() {
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
PageController.showBusyIndicator(false)
}
}
QtObject {
id: reboot
property bool isVisible: true
readonly property string title: qsTr("Reboot server")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to reboot the server?")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reboot server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
QtObject {
id: remove
property bool isVisible: true
readonly property string title: qsTr("Remove server from application")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to remove the server from application?")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot remove server during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
QtObject {
id: clear
property bool isVisible: true
readonly property string title: qsTr("Clear server from Amnezia software")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot clear server from Amnezia software during active connection"))
} else {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers()
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
QtObject {
id: reset
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
readonly property string title: qsTr("Reset API config")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot reset API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false)
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
QtObject {
id: switch_to_premium
property bool isVisible: ServersModel.getProcessedServerData("isServerFromTelegramApi")
readonly property string title: qsTr("Switch to the new Amnezia Premium subscription")
readonly property string description: ""
readonly property var tColor: AmneziaStyle.color.vibrantRed
readonly property var clickedHandler: function() {
PageController.goToPageHome()
ApiPremV1MigrationController.showMigrationDrawer()
}
}
} }

View file

@ -21,249 +21,213 @@ PageType {
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
ColumnLayout { BackButtonType {
id: header id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
BackButtonType { ListViewType {
id: backButton id: listView
}
BaseHeaderType { anchors.top: backButton.bottom
Layout.fillWidth: true anchors.bottom: parent.bottom
Layout.leftMargin: 16 anchors.right: parent.right
Layout.rightMargin: 16 anchors.left: parent.left
Layout.bottomMargin: 32
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings") header: ColumnLayout {
} width: listView.width
ListView { BaseHeaderType {
id: protocols Layout.fillWidth: true
Layout.fillWidth: true Layout.leftMargin: 16
height: protocols.contentItem.height Layout.rightMargin: 16
clip: true Layout.bottomMargin: 32
interactive: true
property bool isFocusable: true headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: ProtocolsModel
delegate: Item {
implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout {
id: delegateContent
anchors.fill: parent
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
id: clientSettings
Layout.fillWidth: true
text: protocolName + qsTr(" connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isClientSettingsVisible
clickedFunction: function() {
if (isClientProtocolExists) {
switch (protocolIndex) {
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
}
}
MouseArea {
anchors.fill: clientSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isClientSettingsVisible
}
LabelWithButtonType {
id: serverSettings
Layout.fillWidth: true
text: protocolName + qsTr(" server settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() {
switch (protocolIndex) {
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(serverProtocolPage);
}
MouseArea {
anchors.fill: serverSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isServerSettingsVisible
}
}
}
footer: ColumnLayout {
width: header.width
LabelWithButtonType {
id: clearCacheButton
Layout.fillWidth: true
visible: root.isClearCacheVisible
text: qsTr("Clear profile")
clickedFunction: function() {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
// if (!GC.isMobile()) {
// focusItem.forceActiveFocus()
// }
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: clearCacheButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isClearCacheVisible
}
LabelWithButtonType {
id: removeButton
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
}
} }
} }
model: ProtocolsModel
delegate: ColumnLayout {
id: delegateContent
width: listView.width
property bool isClientSettingsVisible: protocolIndex === ProtocolEnum.WireGuard || protocolIndex === ProtocolEnum.Awg
property bool isServerSettingsVisible: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
id: clientSettings
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: protocolName + qsTr(" connection settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isClientSettingsVisible
clickedFunction: function() {
if (isClientProtocolExists) {
switch (protocolIndex) {
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(clientProtocolPage);
} else {
PageController.showNotificationMessage(qsTr("Click the \"connect\" button to create a connection configuration"))
}
}
MouseArea {
anchors.fill: clientSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isClientSettingsVisible
}
LabelWithButtonType {
id: serverSettings
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: protocolName + qsTr(" server settings")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
visible: delegateContent.isServerSettingsVisible
clickedFunction: function() {
switch (protocolIndex) {
case ProtocolEnum.OpenVpn: OpenVpnConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.ShadowSocks: ShadowSocksConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Cloak: CloakConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.WireGuard: WireGuardConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Awg: AwgConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Xray: XrayConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Sftp: SftpConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Ipsec: Ikev2ConfigModel.updateModel(ProtocolsModel.getConfig()); break;
case ProtocolEnum.Socks5Proxy: Socks5ProxyConfigModel.updateModel(ProtocolsModel.getConfig()); break;
}
PageController.goToPage(serverProtocolPage);
}
MouseArea {
anchors.fill: serverSettings
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: delegateContent.isServerSettingsVisible
}
}
footer: ColumnLayout {
width: listView.width
LabelWithButtonType {
id: clearCacheButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isClearCacheVisible
text: qsTr("Clear profile")
clickedFunction: function() {
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: clearCacheButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: root.isClearCacheVisible
}
LabelWithButtonType {
id: removeButton
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
visible: ServersModel.isProcessedServerHasWriteAccess()
}
}
} }
} }

View file

@ -40,25 +40,20 @@ PageType {
} }
} }
ListView { ListViewType {
id: servers id: servers
objectName: "servers" objectName: "servers"
width: parent.width width: parent.width
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: 500
property bool isFocusable: true
model: ServersModel model: ServersModel
clip: true
reuseItems: true
delegate: Item { delegate: Item {
implicitWidth: servers.width implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight

View file

@ -1,445 +1,450 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import QtCore import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ProtocolEnum 1.0 import ProtocolEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi") property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi")
property bool pageEnabled property bool pageEnabled
Component.onCompleted: { Component.onCompleted: {
if (ConnectionController.isConnected) { if (ConnectionController.isConnected) {
PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection")) PageController.showNotificationMessage(qsTr("Cannot change split tunneling settings during active connection"))
root.pageEnabled = false root.pageEnabled = false
} else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) { } else if (ServersModel.isDefaultServerDefaultContainerHasSplitTunneling) {
PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function")) PageController.showNotificationMessage(qsTr("Default server does not support split tunneling function"))
root.pageEnabled = false root.pageEnabled = false
} else { } else {
root.pageEnabled = true root.pageEnabled = true
} }
} }
Connections { Connections {
target: SitesController target: SitesController
function onFinished(message) { function onFinished(message) {
PageController.showNotificationMessage(message) PageController.showNotificationMessage(message)
} }
function onErrorOccurred(errorMessage) { function onErrorOccurred(errorMessage) {
PageController.showErrorMessage(errorMessage) PageController.showErrorMessage(errorMessage)
} }
} }
QtObject { QtObject {
id: routeMode id: routeMode
property int allSites: 0 property int allSites: 0
property int onlyForwardSites: 1 property int onlyForwardSites: 1
property int allExceptSites: 2 property int allExceptSites: 2
} }
property list<QtObject> routeModesModel: [ property list<QtObject> routeModesModel: [
onlyForwardSites, onlyForwardSites,
allExceptSites allExceptSites
] ]
QtObject { QtObject {
id: onlyForwardSites id: onlyForwardSites
property string name: qsTr("Only the sites listed here will be accessed through the VPN") property string name: qsTr("Only the sites listed here will be accessed through the VPN")
property int type: routeMode.onlyForwardSites property int type: routeMode.onlyForwardSites
} }
QtObject { QtObject {
id: allExceptSites id: allExceptSites
property string name: qsTr("Addresses from the list should not be accessed via VPN") property string name: qsTr("Addresses from the list should not be accessed via VPN")
property int type: routeMode.allExceptSites property int type: routeMode.allExceptSites
} }
function getRouteModesModelIndex() { function getRouteModesModelIndex() {
var currentRouteMode = SitesModel.routeMode var currentRouteMode = SitesModel.routeMode
if ((routeMode.onlyForwardSites === currentRouteMode) || (routeMode.allSites === currentRouteMode)) { if ((routeMode.onlyForwardSites === currentRouteMode) || (routeMode.allSites === currentRouteMode)) {
return 0 return 0
} else if (routeMode.allExceptSites === currentRouteMode) { } else if (routeMode.allExceptSites === currentRouteMode) {
return 1 return 1
} }
} }
ColumnLayout { ColumnLayout {
id: header id: header
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
BackButtonType { BackButtonType {
id: backButton id: backButton
} }
HeaderTypeWithSwitcher { HeaderTypeWithSwitcher {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
headerText: qsTr("Split tunneling") headerText: qsTr("Split tunneling")
enabled: root.pageEnabled enabled: root.pageEnabled
showSwitcher: true showSwitcher: true
switcher { switcher {
checked: SitesModel.isTunnelingEnabled checked: SitesModel.isTunnelingEnabled
enabled: root.pageEnabled enabled: root.pageEnabled
} }
switcherFunction: function(checked) { switcherFunction: function(checked) {
SitesModel.toggleSplitTunneling(checked) SitesModel.toggleSplitTunneling(checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name selector.text = root.routeModesModel[getRouteModesModelIndex()].name
} }
} }
DropDownType { DropDownType {
id: selector id: selector
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
drawerHeight: 0.4375 drawerHeight: 0.4375
drawerParent: root drawerParent: root
enabled: root.pageEnabled enabled: root.pageEnabled
headerText: qsTr("Mode") headerText: qsTr("Mode")
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
rootWidth: root.width rootWidth: root.width
model: root.routeModesModel model: root.routeModesModel
selectedIndex: getRouteModesModelIndex() selectedIndex: getRouteModesModelIndex()
clickedFunction: function() { clickedFunction: function() {
selector.text = selectedText selector.text = selectedText
selector.closeTriggered() selector.closeTriggered()
if (SitesModel.routeMode !== root.routeModesModel[selectedIndex].type) { if (SitesModel.routeMode !== root.routeModesModel[selectedIndex].type) {
SitesModel.routeMode = root.routeModesModel[selectedIndex].type SitesModel.routeMode = root.routeModesModel[selectedIndex].type
} }
} }
Component.onCompleted: { Component.onCompleted: {
if (root.routeModesModel[selectedIndex].type === SitesModel.routeMode) { if (root.routeModesModel[selectedIndex].type === SitesModel.routeMode) {
selector.text = selectedText selector.text = selectedText
} else { } else {
selector.text = root.routeModesModel[0].name selector.text = root.routeModesModel[0].name
} }
} }
Connections { Connections {
target: SitesModel target: SitesModel
function onRouteModeChanged() { function onRouteModeChanged() {
selectedIndex = getRouteModesModelIndex() selectedIndex = getRouteModesModelIndex()
} }
} }
} }
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
anchors.bottom: addSiteButton.top anchors.bottom: addSiteButton.top
width: parent.width width: parent.width
enabled: root.pageEnabled enabled: root.pageEnabled
property bool isFocusable: true model: SortFilterProxyModel {
id: proxySitesModel
model: SortFilterProxyModel { sourceModel: SitesModel
id: proxySitesModel filters: [
sourceModel: SitesModel AnyOf {
filters: [ RegExpFilter {
AnyOf { roleName: "url"
RegExpFilter { pattern: ".*" + searchField.textField.text + ".*"
roleName: "url" caseSensitivity: Qt.CaseInsensitive
pattern: ".*" + searchField.textField.text + ".*" }
caseSensitivity: Qt.CaseInsensitive RegExpFilter {
} roleName: "ip"
RegExpFilter { pattern: ".*" + searchField.textField.text + ".*"
roleName: "ip" caseSensitivity: Qt.CaseInsensitive
pattern: ".*" + searchField.textField.text + ".*" }
caseSensitivity: Qt.CaseInsensitive }
} ]
} }
]
} delegate: ColumnLayout {
width: listView.width
clip: true
LabelWithButtonType {
reuseItems: true id: site
Layout.fillWidth: true
delegate: ColumnLayout {
id: delegateContent text: url
descriptionText: ip
width: listView.width rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
LabelWithButtonType {
id: site clickedFunction: function() {
Layout.fillWidth: true var headerText = qsTr("Remove ") + url + "?"
var yesButtonText = qsTr("Continue")
text: url var noButtonText = qsTr("Cancel")
descriptionText: ip
rightImageSource: "qrc:/images/controls/trash.svg" var yesButtonFunction = function() {
rightImageColor: AmneziaStyle.color.paleGray SitesController.removeSite(proxySitesModel.mapToSource(index))
if (!GC.isMobile()) {
clickedFunction: function() { site.rightButton.forceActiveFocus()
var headerText = qsTr("Remove ") + url + "?" }
var yesButtonText = qsTr("Continue") }
var noButtonText = qsTr("Cancel") var noButtonFunction = function() {
if (!GC.isMobile()) {
var yesButtonFunction = function() { site.rightButton.forceActiveFocus()
SitesController.removeSite(proxySitesModel.mapToSource(index)) }
if (!GC.isMobile()) { }
site.rightButton.forceActiveFocus()
} showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
var noButtonFunction = function() { }
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus() DividerType {}
} }
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) Rectangle {
} anchors.fill: addSiteButton
} anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
DividerType {} opacity: 0.8
} }
}
RowLayout {
id: addSiteButton
Rectangle {
anchors.fill: addSiteButton enabled: root.pageEnabled
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack anchors.bottom: parent.bottom
opacity: 0.8 anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 24
RowLayout { anchors.rightMargin: 16
id: addSiteButton anchors.leftMargin: 16
anchors.bottomMargin: 24
enabled: root.pageEnabled
TextFieldWithHeaderType {
anchors.bottom: parent.bottom id: searchField
anchors.left: parent.left
anchors.right: parent.right Layout.fillWidth: true
anchors.topMargin: 24 rightButtonClickedOnEnter: true
anchors.rightMargin: 16
anchors.leftMargin: 16 textField.placeholderText: qsTr("website or IP")
anchors.bottomMargin: 24 buttonImageSource: "qrc:/images/controls/plus.svg"
TextFieldWithHeaderType { clickedFunc: function() {
id: searchField PageController.showBusyIndicator(true)
SitesController.addSite(textField.text)
Layout.fillWidth: true textField.text = ""
rightButtonClickedOnEnter: true PageController.showBusyIndicator(false)
}
textField.placeholderText: qsTr("website or IP") }
buttonImageSource: "qrc:/images/controls/plus.svg"
ImageButtonType {
clickedFunc: function() { id: addSiteButtonImage
PageController.showBusyIndicator(true) implicitWidth: 56
SitesController.addSite(textField.text) implicitHeight: 56
textField.text = ""
PageController.showBusyIndicator(false) image: "qrc:/images/controls/more-vertical.svg"
} imageColor: AmneziaStyle.color.paleGray
}
onClicked: function () {
ImageButtonType { moreActionsDrawer.openTriggered()
id: addSiteButtonImage }
implicitWidth: 56
implicitHeight: 56 Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
image: "qrc:/images/controls/more-vertical.svg" }
imageColor: AmneziaStyle.color.paleGray }
onClicked: function () { DrawerType2 {
moreActionsDrawer.openTriggered() id: moreActionsDrawer
}
anchors.fill: parent
Keys.onReturnPressed: addSiteButtonImage.clicked() expandedHeight: parent.height * 0.4375
Keys.onEnterPressed: addSiteButtonImage.clicked()
} expandedStateContent: ColumnLayout {
} id: moreActionsDrawerContent
DrawerType2 { anchors.top: parent.top
id: moreActionsDrawer anchors.left: parent.left
anchors.right: parent.right
anchors.fill: parent
expandedHeight: parent.height * 0.4375 Header2Type {
Layout.fillWidth: true
expandedStateContent: ColumnLayout { Layout.margins: 16
id: moreActionsDrawerContent
headerText: qsTr("Import / Export Sites")
anchors.top: parent.top }
anchors.left: parent.left
anchors.right: parent.right LabelWithButtonType {
id: importSitesButton
Header2Type { Layout.fillWidth: true
Layout.fillWidth: true
Layout.margins: 16 text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
headerText: qsTr("Import / Export Sites")
} clickedFunction: function() {
importSitesDrawer.openTriggered()
LabelWithButtonType { }
id: importSitesButton }
Layout.fillWidth: true
DividerType {}
text: qsTr("Import")
rightImageSource: "qrc:/images/controls/chevron-right.svg" LabelWithButtonType {
id: exportSitesButton
clickedFunction: function() { Layout.fillWidth: true
importSitesDrawer.openTriggered() text: qsTr("Save site list")
}
} clickedFunction: function() {
var fileName = ""
DividerType {} if (GC.isMobile()) {
fileName = "amnezia_sites.json"
LabelWithButtonType { } else {
id: exportSitesButton fileName = SystemController.getFileName(qsTr("Save sites"),
Layout.fillWidth: true qsTr("Sites files (*.json)"),
text: qsTr("Save site list") StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites",
true,
clickedFunction: function() { ".json")
var fileName = "" }
if (GC.isMobile()) { if (fileName !== "") {
fileName = "amnezia_sites.json" PageController.showBusyIndicator(true)
} else { SitesController.exportSites(fileName)
fileName = SystemController.getFileName(qsTr("Save sites"), moreActionsDrawer.closeTriggered()
qsTr("Sites files (*.json)"), PageController.showBusyIndicator(false)
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/amnezia_sites", }
true, }
".json") }
}
if (fileName !== "") { DividerType {}
PageController.showBusyIndicator(true) }
SitesController.exportSites(fileName) }
moreActionsDrawer.closeTriggered()
PageController.showBusyIndicator(false) DrawerType2 {
} id: importSitesDrawer
}
} anchors.fill: parent
expandedHeight: parent.height * 0.4375
DividerType {}
} expandedStateContent: Item {
} implicitHeight: importSitesDrawer.expandedHeight
DrawerType2 { BackButtonType {
id: importSitesDrawer id: importSitesDrawerBackButton
anchors.fill: parent anchors.top: parent.top
expandedHeight: parent.height * 0.4375 anchors.left: parent.left
anchors.right: parent.right
expandedStateContent: Item { anchors.topMargin: 16
implicitHeight: importSitesDrawer.expandedHeight
backButtonFunction: function() {
BackButtonType { importSitesDrawer.closeTriggered()
id: importSitesDrawerBackButton }
}
anchors.top: parent.top
anchors.left: parent.left ListViewType {
anchors.right: parent.right id: importSitesDrawerListView
anchors.topMargin: 16
anchors.top: importSitesDrawerBackButton.bottom
backButtonFunction: function() { anchors.left: parent.left
importSitesDrawer.closeTriggered() anchors.right: parent.right
} anchors.bottom: parent.bottom
}
header: ColumnLayout {
FlickableType { width: importSitesDrawerListView.width
anchors.top: importSitesDrawerBackButton.bottom
anchors.left: parent.left Header2Type {
anchors.right: parent.right Layout.fillWidth: true
anchors.bottom: parent.bottom Layout.margins: 16
contentHeight: importSitesDrawerContent.height headerText: qsTr("Import a list of sites")
}
ColumnLayout { }
id: importSitesDrawerContent
model: importOptions
anchors.top: parent.top
anchors.left: parent.left delegate: ColumnLayout {
anchors.right: parent.right width: importSitesDrawerListView.width
Header2Type { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Import a list of sites")
} text: title
LabelWithButtonType { clickedFunction: function() {
id: importSitesButton2 clickedHandler()
Layout.fillWidth: true }
}
text: qsTr("Replace site list")
DividerType {}
clickedFunction: function() { }
var fileName = SystemController.getFileName(qsTr("Open sites file"), }
qsTr("Sites files (*.json)")) }
if (fileName !== "") { }
importSitesDrawerContent.importSites(fileName, true)
} property list<QtObject> importOptions: [
} replaceOption,
} addOption,
]
DividerType {}
QtObject {
LabelWithButtonType { id: replaceOption
id: importSitesButton3
Layout.fillWidth: true readonly property string title: qsTr("Replace site list")
text: qsTr("Add imported sites to existing ones") readonly property var clickedHandler: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
clickedFunction: function() { qsTr("Sites files (*.json)"))
var fileName = SystemController.getFileName(qsTr("Open sites file"), if (fileName !== "") {
qsTr("Sites files (*.json)")) importSitesDrawerContent.importSites(fileName, true)
if (fileName !== "") { }
importSitesDrawerContent.importSites(fileName, false) }
} }
}
} QtObject {
id: addOption
function importSites(fileName, replaceExistingSites) {
PageController.showBusyIndicator(true) readonly property string title: qsTr("Add imported sites to existing ones")
SitesController.importSites(fileName, replaceExistingSites) readonly property var clickedHandler: function() {
PageController.showBusyIndicator(false) var fileName = SystemController.getFileName(qsTr("Open sites file"),
importSitesDrawer.closeTriggered() qsTr("Sites files (*.json)"))
moreActionsDrawer.closeTriggered() if (fileName !== "") {
} importSitesDrawerContent.importSites(fileName, false)
}
DividerType {} }
} }
}
} function importSites(fileName, replaceExistingSites) {
} PageController.showBusyIndicator(true)
} SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false)
importSitesDrawer.closeTriggered()
moreActionsDrawer.closeTriggered()
}
}

View file

@ -1,146 +1,177 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
FlickableType { BackButtonType {
id: fl id: backButton
anchors.top: parent.top
anchors.bottom: parent.bottom anchors.top: parent.top
contentHeight: content.height + continueButton.implicitHeight + continueButton.anchors.bottomMargin + continueButton.anchors.topMargin anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { anchors.topMargin: 20
id: content
onFocusChanged: {
anchors.top: parent.top if (this.activeFocus) {
anchors.left: parent.left listView.positionViewAtBeginning()
anchors.right: parent.right }
}
spacing: 0 }
BackButtonType { ListViewType {
id: backButton id: listView
Layout.topMargin: 20
} anchors.top: backButton.bottom
anchors.bottom: parent.bottom
BaseHeaderType { anchors.right: parent.right
Layout.fillWidth: true anchors.left: parent.left
Layout.topMargin: 8
Layout.rightMargin: 16 header: ColumnLayout {
Layout.leftMargin: 16 width: listView.width
Layout.bottomMargin: 32
BaseHeaderType {
headerText: ApiServicesModel.getSelectedServiceData("name") Layout.fillWidth: true
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") Layout.topMargin: 8
} Layout.rightMargin: 16
Layout.leftMargin: 16
LabelWithImageType { Layout.bottomMargin: 32
Layout.fillWidth: true
Layout.margins: 16 headerText: ApiServicesModel.getSelectedServiceData("name")
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
imageSource: "qrc:/images/controls/map-pin.svg" }
leftText: qsTr("For the region") }
rightText: ApiServicesModel.getSelectedServiceData("region")
} model: inputFields
spacing: 0
LabelWithImageType {
Layout.fillWidth: true delegate: ColumnLayout {
Layout.margins: 16 width: listView.width
imageSource: "qrc:/images/controls/tag.svg" LabelWithImageType {
leftText: qsTr("Price") Layout.fillWidth: true
rightText: ApiServicesModel.getSelectedServiceData("price") Layout.margins: 16
}
imageSource: imagePath
LabelWithImageType { leftText: lText
Layout.fillWidth: true rightText: rText
Layout.margins: 16 }
}
imageSource: "qrc:/images/controls/history.svg"
leftText: qsTr("Work period") footer: ColumnLayout {
rightText: ApiServicesModel.getSelectedServiceData("timeLimit") width: listView.width
visible: rightText !== "" spacing: 0
}
ParagraphTextType {
LabelWithImageType { Layout.fillWidth: true
Layout.fillWidth: true Layout.rightMargin: 16
Layout.margins: 16 Layout.leftMargin: 16
imageSource: "qrc:/images/controls/gauge.svg" onLinkActivated: function(link) {
leftText: qsTr("Speed") Qt.openUrlExternally(link)
rightText: ApiServicesModel.getSelectedServiceData("speed") }
} textFormat: Text.RichText
text: {
LabelWithImageType { var text = ApiServicesModel.getSelectedServiceData("features")
Layout.fillWidth: true return text.replace("%1", LanguageModel.getCurrentSiteUrl())
Layout.margins: 16 }
imageSource: "qrc:/images/controls/info.svg" MouseArea {
leftText: qsTr("Features") anchors.fill: parent
rightText: "" acceptedButtons: Qt.NoButton
} cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
ParagraphTextType { }
Layout.fillWidth: true
Layout.rightMargin: 16 BasicButtonType {
Layout.leftMargin: 16 id: continueButton
onLinkActivated: function(link) { Layout.fillWidth: true
Qt.openUrlExternally(link) Layout.topMargin: 32
} Layout.bottomMargin: 32
textFormat: Text.RichText Layout.leftMargin: 16
text: { Layout.rightMargin: 16
var text = ApiServicesModel.getSelectedServiceData("features")
return text.replace("%1", LanguageModel.getCurrentSiteUrl()) text: qsTr("Connect")
}
clickedFunc: function() {
MouseArea { var endpoint = ApiServicesModel.getStoreEndpoint()
anchors.fill: parent if (endpoint !== undefined && endpoint !== "") {
acceptedButtons: Qt.NoButton Qt.openUrlExternally(endpoint)
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor PageController.closePage()
} PageController.closePage()
} } else {
} PageController.showBusyIndicator(true)
} ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false)
BasicButtonType { }
id: continueButton }
}
anchors.right: parent.right }
anchors.left: parent.left }
anchors.bottom: parent.bottom
property list<QtObject> inputFields: [
anchors.topMargin: 32 region,
anchors.rightMargin: 16 price,
anchors.leftMargin: 16 timeLimit,
anchors.bottomMargin: 32 speed,
features
text: qsTr("Connect") ]
clickedFunc: function() { QtObject {
var endpoint = ApiServicesModel.getStoreEndpoint() id: region
if (endpoint !== undefined && endpoint !== "") {
Qt.openUrlExternally(endpoint) readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
PageController.closePage() readonly property string lText: qsTr("For the region")
PageController.closePage() readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
} else { property bool isVisible: true
PageController.showBusyIndicator(true) }
ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false) QtObject {
} id: price
}
} readonly property string imagePath: "qrc:/images/controls/tag.svg"
} readonly property string lText: qsTr("Price")
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
property bool isVisible: true
}
QtObject {
id: timeLimit
readonly property string imagePath: "qrc:/images/controls/history.svg"
readonly property string lText: qsTr("Work period")
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
property bool isVisible: rText !== ""
}
QtObject {
id: speed
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
readonly property string lText: qsTr("Speed")
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
property bool isVisible: true
}
QtObject {
id: features
readonly property string imagePath: "qrc:/images/controls/info.svg"
readonly property string lText: qsTr("Features")
readonly property string rText: ""
property bool isVisible: true
}
}

View file

@ -40,7 +40,7 @@ PageType {
} }
} }
ListView { ListViewType {
id: servicesListView id: servicesListView
anchors.top: header.bottom anchors.top: header.bottom
@ -48,17 +48,11 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: 16 anchors.topMargin: 16
spacing: 0 spacing: 0
property bool isFocusable: true
clip: true
reuseItems: true
model: ApiServicesModel model: ApiServicesModel
ScrollBar.vertical: ScrollBarType {}
delegate: Item { delegate: Item {
implicitWidth: servicesListView.width implicitWidth: servicesListView.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight

View file

@ -27,21 +27,13 @@ PageType {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.fill: parent anchors.fill: parent
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
model: variants model: variants
clip: true
reuseItems: true
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width

View file

@ -28,41 +28,14 @@ PageType {
} }
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -78,8 +51,6 @@ PageType {
model: inputFields model: inputFields
spacing: 16 spacing: 16
clip: true
reuseItems: true
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width

View file

@ -20,6 +20,7 @@ PageType {
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@ -42,102 +43,88 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
FlickableType { ButtonGroup {
id: fl id: buttonGroup
}
ListViewType {
id: listView
property int dockerContainer
property int containerDefaultPort
property int containerDefaultTransportProto
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + setupLaterButton.anchors.bottomMargin anchors.left: parent.left
anchors.right: parent.right
Column { spacing: 16
header: ColumnLayout {
id: content id: content
anchors.top: parent.top width: listView.width
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 16 spacing: 16
BaseHeaderType { BaseHeaderType {
id: header id: header
implicitWidth: parent.width Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerTextMaximumLineCount: 10 headerTextMaximumLineCount: 10
headerText: qsTr("Choose Installation Type") headerText: qsTr("Choose Installation Type")
} }
}
ButtonGroup { model: proxyContainersModel
id: buttonGroup currentIndex: 1
}
ListView { delegate: ColumnLayout {
id: containers
width: parent.width
height: containers.contentItem.height
spacing: 16
currentIndex: 0 width: listView.width
clip: true
interactive: false
model: proxyContainersModel
property int dockerContainer CardType {
property int containerDefaultPort id: card
property int containerDefaultTransportProto
property bool isFocusable: true Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
delegate: Item { headerText: easySetupHeader
implicitWidth: containers.width bodyText: easySetupDescription
implicitHeight: delegateContent.implicitHeight
ColumnLayout { ButtonGroup.group: buttonGroup
id: delegateContent
anchors.top: parent.top onClicked: function() {
anchors.left: parent.left isEasySetup = true
anchors.right: parent.right var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
CardType { listView.dockerContainer = dockerContainer
id: card listView.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
listView.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
Layout.fillWidth: true
headerText: easySetupHeader
bodyText: easySetupDescription
ButtonGroup.group: buttonGroup
onClicked: function() {
isEasySetup = true
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
containers.dockerContainer = dockerContainer
containers.containerDefaultPort = ProtocolProps.getPortForInstall(defaultContainerProto)
containers.containerDefaultTransportProto = ProtocolProps.defaultTransportProto(defaultContainerProto)
}
}
}
}
Component.onCompleted: {
var item = containers.itemAtIndex(containers.currentIndex)
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
} }
} }
}
footer: ColumnLayout {
width: listView.width
DividerType { DividerType {
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
} }
CardType { CardType {
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Manual") headerText: qsTr("Manual")
bodyText: qsTr("Choose a VPN protocol") bodyText: qsTr("Choose a VPN protocol")
@ -152,19 +139,19 @@ PageType {
BasicButtonType { BasicButtonType {
id: continueButton id: continueButton
implicitWidth: parent.width Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Continue") text: qsTr("Continue")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
if (root.isEasySetup) { if (root.isEasySetup) {
ContainersModel.setProcessedContainerIndex(containers.dockerContainer) ContainersModel.setProcessedContainerIndex(listView.dockerContainer)
PageController.goToPage(PageEnum.PageSetupWizardInstalling) PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.install(containers.dockerContainer, InstallController.install(listView.dockerContainer,
containers.containerDefaultPort, listView.containerDefaultPort,
containers.containerDefaultTransportProto) listView.containerDefaultTransportProto)
} else { } else {
PageController.goToPage(PageEnum.PageSetupWizardProtocols) PageController.goToPage(PageEnum.PageSetupWizardProtocols)
} }
@ -174,9 +161,11 @@ PageType {
BasicButtonType { BasicButtonType {
id: setupLaterButton id: setupLaterButton
implicitWidth: parent.width Layout.fillWidth: true
anchors.topMargin: 8 Layout.topMargin: 8
anchors.bottomMargin: 24 Layout.bottomMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -185,9 +174,6 @@ PageType {
textColor: AmneziaStyle.color.paleGray textColor: AmneziaStyle.color.paleGray
borderWidth: 1 borderWidth: 1
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: fl
visible: { visible: {
if (PageController.isTriggeredByConnectButton()) { if (PageController.isTriggeredByConnectButton()) {
PageController.setTriggeredByConnectButton(false) PageController.setTriggeredByConnectButton(false)
@ -205,5 +191,15 @@ PageType {
} }
} }
} }
Component.onCompleted: {
var item = listView.itemAtIndex(listView.currentIndex)
if (item !== null) {
var button = item.children[0].children[0]
button.checked = true
button.clicked()
}
}
} }
} }

View file

@ -85,92 +85,76 @@ PageType {
] ]
} }
FlickableType { ListViewType {
id: listView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height
Column { currentIndex: -1
id: content
anchors.top: parent.top model: proxyContainersModel
anchors.left: parent.left
anchors.right: parent.right
spacing: 16 delegate: ColumnLayout {
width: listView.width
ListView { BaseHeaderType {
id: container Layout.fillWidth: true
width: parent.width Layout.topMargin: 20
height: container.contentItem.height Layout.leftMargin: 16
currentIndex: -1 Layout.rightMargin: 16
clip: true
interactive: false
model: proxyContainersModel
delegate: Item { headerText: qsTr("Installing")
implicitWidth: container.width descriptionText: name
implicitHeight: delegateContent.implicitHeight }
ColumnLayout { ProgressBarType {
id: delegateContent id: progressBar
anchors.fill: parent Layout.fillWidth: true
anchors.rightMargin: 16 Layout.topMargin: 32
anchors.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16
BaseHeaderType { Timer {
Layout.fillWidth: true id: timer
Layout.topMargin: 20
headerText: qsTr("Installing") interval: 300
descriptionText: name repeat: true
} running: root.isTimerRunning
onTriggered: {
ProgressBarType { progressBar.value += 0.003
id: progressBar
Layout.fillWidth: true
Layout.topMargin: 32
Timer {
id: timer
interval: 300
repeat: true
running: root.isTimerRunning
onTriggered: {
progressBar.value += 0.003
}
}
}
ParagraphTextType {
id: progressText
Layout.fillWidth: true
Layout.topMargin: 8
text: root.progressBarText
}
BasicButtonType {
id: cancelIntallationButton
Layout.fillWidth: true
Layout.topMargin: 24
visible: root.isCancelButtonVisible
text: qsTr("Cancel installation")
clickedFunc: function() {
InstallController.cancelInstallation()
PageController.showBusyIndicator(true)
}
}
} }
} }
} }
ParagraphTextType {
id: progressText
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: root.progressBarText
}
BasicButtonType {
id: cancelIntallationButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isCancelButtonVisible
text: qsTr("Cancel installation")
clickedFunc: function() {
InstallController.cancelInstallation()
PageController.showBusyIndicator(true)
}
}
} }
} }
} }

View file

@ -29,256 +29,241 @@ PageType {
] ]
} }
FlickableType { BackButtonType {
anchors.fill: parent id: backButton
contentHeight: content.height
Column { anchors.top: parent.top
id: content anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.top: parent.top onFocusChanged: {
anchors.left: parent.left if (this.activeFocus) {
anchors.right: parent.right listView.positionViewAtBeginning()
}
}
}
ListView { ListViewType {
id: processedContainerListView id: listView
width: parent.width
height: contentItem.height
currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
property bool isFocusable: true anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
Keys.onTabPressed: { header: ColumnLayout {
FocusController.nextKeyTabItem() width: listView.width
BaseHeaderType {
id: header
Layout.fillWidth: true
headerText: qsTr("Installing %1").arg(name)
descriptionText: description
}
}
currentIndex: -1
model: proxyContainersModel
delegate: ColumnLayout {
width: listView.width
BasicButtonType {
id: showDetailsButton
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("More detailed")
clickedFunc: function() {
showDetailsDrawer.openTriggered()
} }
}
Keys.onBacktabPressed: { DrawerType2 {
FocusController.previousKeyTabItem() id: showDetailsDrawer
} parent: root
Keys.onUpPressed: { anchors.fill: parent
FocusController.nextKeyUpItem() expandedHeight: parent.height * 0.9
} expandedStateContent: Item {
implicitHeight: showDetailsDrawer.expandedHeight
Keys.onDownPressed: { BackButtonType {
FocusController.nextKeyDownItem() id: showDetailsBackButton
}
Keys.onLeftPressed: { anchors.top: parent.top
FocusController.nextKeyLeftItem() anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
Keys.onRightPressed: { backButtonFunction: function() {
FocusController.nextKeyRightItem() showDetailsDrawer.closeTriggered()
}
delegate: Item {
implicitWidth: processedContainerListView.width
implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height
property alias port:port
ColumnLayout {
id: delegateContent
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
Layout.rightMargin: -16
Layout.leftMargin: -16
} }
}
BaseHeaderType { ListViewType {
id: header id: showDetailsListView
Layout.fillWidth: true anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
headerText: qsTr("Installing %1").arg(name) header: ColumnLayout {
descriptionText: description width: showDetailsListView.width
}
BasicButtonType { Header2Type {
id: showDetailsButton id: showDetailsDrawerHeader
Layout.topMargin: 16 Layout.fillWidth: true
Layout.leftMargin: -8 Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: 32 headerText: name
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("More detailed")
KeyNavigation.tab: transportProtoSelector
clickedFunc: function() {
showDetailsDrawer.openTriggered()
} }
} }
DrawerType2 { model: 1 // fake model to force the ListView to be created without a model
id: showDetailsDrawer
parent: root
anchors.fill: parent delegate: ColumnLayout {
expandedHeight: parent.height * 0.9 width: showDetailsListView.width
expandedStateContent: Item {
implicitHeight: showDetailsDrawer.expandedHeight
BackButtonType { ParagraphTextType {
id: showDetailsBackButton Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
anchors.top: parent.top text: detailedDescription
anchors.left: parent.left textFormat: Text.MarkdownText
anchors.right: parent.right }
anchors.topMargin: 16
backButtonFunction: function() { Rectangle {
showDetailsDrawer.closeTriggered() Layout.fillHeight: true
} Layout.leftMargin: 16
} Layout.rightMargin: 16
FlickableType { color: AmneziaStyle.color.transparent
id: fl }
anchors.top: showDetailsBackButton.bottom }
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: {
var emptySpaceHeight = parent.height - showDetailsBackButton.implicitHeight - showDetailsBackButton.anchors.topMargin
return (showDetailsDrawerContent.height > emptySpaceHeight) ?
showDetailsDrawerContent.height : emptySpaceHeight
}
ColumnLayout { footer: ColumnLayout {
id: showDetailsDrawerContent width: showDetailsListView.width
anchors.top: parent.top BasicButtonType {
anchors.left: parent.left id: showDetailsCloseButton
anchors.right: parent.right Layout.fillWidth: true
anchors.rightMargin: 16 Layout.bottomMargin: 32
anchors.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16
Header2Type { text: qsTr("Close")
id: showDetailsDrawerHeader
Layout.fillWidth: true
Layout.topMargin: 16
headerText: name clickedFunc: function() {
} showDetailsDrawer.closeTriggered()
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
text: detailedDescription
textFormat: Text.MarkdownText
}
Rectangle {
Layout.fillHeight: true
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: showDetailsCloseButton
Layout.fillWidth: true
Layout.bottomMargin: 32
parentFlickable: fl
text: qsTr("Close")
clickedFunc: function() {
showDetailsDrawer.closeTriggered()
}
}
}
} }
} }
} }
ParagraphTextType {
id: transportProtoHeader
Layout.topMargin: 16
text: qsTr("Network protocol")
}
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
rootWidth: root.width
}
TextFieldWithHeaderType {
id: port
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Port")
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
}
Rectangle {
Layout.fillHeight: true
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: installButton
Layout.fillWidth: true
Layout.bottomMargin: 32
text: qsTr("Install")
clickedFunc: function() {
if (!port.textField.acceptableInput &&
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
port.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
}
}
Component.onCompleted: {
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
port.visible = false
} else {
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
}
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
}
} }
} }
} }
ParagraphTextType {
id: transportProtoHeader
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Network protocol")
}
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
rootWidth: root.width
}
TextFieldWithHeaderType {
id: port
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Port")
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
}
Rectangle {
Layout.fillHeight: true
Layout.rightMargin: 16
Layout.leftMargin: 16
color: AmneziaStyle.color.transparent
}
BasicButtonType {
id: installButton
Layout.fillWidth: true
Layout.bottomMargin: 32
Layout.rightMargin: 16
Layout.leftMargin: 16
text: qsTr("Install")
clickedFunc: function() {
if (!port.textField.acceptableInput &&
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
ContainerProps.containerTypeToString(dockerContainer) !== "ikev2") {
port.errorText = qsTr("The port must be in the range of 1 to 65535")
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textField.text, transportProtoSelector.currentIndex)
}
}
Component.onCompleted: {
var defaultContainerProto = ContainerProps.defaultProtocol(dockerContainer)
if (ProtocolProps.defaultPort(defaultContainerProto) < 0) {
port.visible = false
} else {
port.textField.text = ProtocolProps.getPortForInstall(defaultContainerProto)
}
transportProtoSelector.currentIndex = ProtocolProps.defaultTransportProto(defaultContainerProto)
port.enabled = ProtocolProps.defaultPortChangeable(defaultContainerProto)
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
}
} }
} }
} }

View file

@ -44,17 +44,13 @@ PageType {
anchors.topMargin: 20 anchors.topMargin: 20
} }
ListView { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
property bool isFocusable: true
ScrollBar.vertical: ScrollBarType {}
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
@ -72,9 +68,8 @@ PageType {
} }
model: proxyContainersModel model: proxyContainersModel
clip: true
spacing: 0 spacing: 0
reuseItems: true
snapMode: ListView.SnapToItem snapMode: ListView.SnapToItem
delegate: ColumnLayout { delegate: ColumnLayout {
@ -87,9 +82,9 @@ PageType {
descriptionText: description descriptionText: description
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function () {
ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index)) ContainersModel.setProcessedContainerIndex(proxyContainersModel.mapToSource(index));
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings);
} }
} }

View file

@ -13,25 +13,31 @@ import "../Config"
PageType { PageType {
id: root id: root
FlickableType { BackButtonType {
id: fl id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.left: parent.left
contentHeight: content.height anchors.right: parent.right
anchors.topMargin: 20
ColumnLayout { onFocusChanged: {
id: content if (this.activeFocus) {
listView.positionViewAtBeginning()
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
} }
}
}
ListViewType {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
header: ColumnLayout {
width: listView.width
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -41,6 +47,13 @@ PageType {
headerText: qsTr("Connection key") headerText: qsTr("Connection key")
descriptionText: qsTr("A line that starts with vpn://...") descriptionText: qsTr("A line that starts with vpn://...")
} }
}
spacing: 16
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: textKey id: textKey
@ -60,23 +73,26 @@ PageType {
} }
} }
} }
}
BasicButtonType { footer: ColumnLayout {
id: continueButton width: listView.width
anchors.bottom: parent.bottom BasicButtonType {
anchors.left: parent.left id: continueButton
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 32
text: qsTr("Continue") Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.topMargin: 16
Layout.bottomMargin: 32
clickedFunc: function() { text: qsTr("Continue")
if (ImportController.extractConfigFromData(textKey.textField.text)) {
PageController.goToPage(PageEnum.PageSetupWizardViewConfig) clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textField.text)) {
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
}
}
} }
} }
} }

View file

@ -46,27 +46,29 @@ PageType {
} }
} }
FlickableType { ListViewType {
id: fl id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + connectButton.implicitHeight anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { header: ColumnLayout {
id: content width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
BaseHeaderType { BaseHeaderType {
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("New connection") headerText: qsTr("New connection")
} }
RowLayout { RowLayout {
Layout.topMargin: 32 Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
spacing: 8 spacing: 8
visible: fileName.text !== "" visible: fileName.text !== ""
@ -88,7 +90,9 @@ PageType {
BasicButtonType { BasicButtonType {
id: showContentButton id: showContentButton
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: -8 Layout.leftMargin: 16
Layout.rightMargin: 16
implicitHeight: 32 implicitHeight: 32
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
@ -99,8 +103,6 @@ PageType {
text: showContent ? qsTr("Collapse content") : qsTr("Show content") text: showContent ? qsTr("Collapse content") : qsTr("Show content")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
showContent = !showContent showContent = !showContent
} }
@ -112,12 +114,23 @@ PageType {
visible: ImportController.isNativeWireGuardConfig() visible: ImportController.isNativeWireGuardConfig()
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.") text: qsTr("Enable WireGuard obfuscation. It may be useful if WireGuard is blocked on your provider.")
} }
}
model: 1 // fake model to force the ListView to be created without a model
delegate: ColumnLayout {
width: listView.width
WarningType { WarningType {
Layout.topMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textString: ImportController.getMaliciousWarningText() textString: ImportController.getMaliciousWarningText()
textFormat: Qt.RichText textFormat: Qt.RichText
@ -130,17 +143,25 @@ PageType {
} }
WarningType { WarningType {
Layout.topMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.") textString: qsTr("Use connection codes only from sources you trust. Codes from public sources may have been created to intercept your data.")
iconPath: "qrc:/images/controls/alert-circle.svg" iconPath: "qrc:/images/controls/alert-circle.svg"
} }
}
footer: ColumnLayout {
width: listView.width
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 48 Layout.bottomMargin: 48
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: configContent.implicitHeight implicitHeight: configContent.implicitHeight
@ -153,42 +174,29 @@ PageType {
id: configContent id: configContent
anchors.fill: parent anchors.fill: parent
anchors.margins: 16
wrapMode: Text.Wrap wrapMode: Text.Wrap
text: ImportController.getConfig() text: ImportController.getConfig()
} }
} }
}
}
Rectangle { BasicButtonType {
anchors.fill: columnContent id: connectButton
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
ColumnLayout { Layout.fillWidth: true
id: columnContent Layout.topMargin: 16
anchors.bottom: parent.bottom Layout.bottomMargin: 32
anchors.left: parent.left Layout.rightMargin: 16
anchors.right: parent.right Layout.leftMargin: 16
anchors.rightMargin: 16
anchors.leftMargin: 16
BasicButtonType { text: qsTr("Connect")
id: connectButton clickedFunc: function() {
Layout.fillWidth: true if (cloakingCheckBox.checked) {
Layout.bottomMargin: 32 ImportController.processNativeWireGuardConfig()
}
text: qsTr("Connect") ImportController.importConfig()
clickedFunc: function() {
if (cloakingCheckBox.checked) {
ImportController.processNativeWireGuardConfig()
} }
ImportController.importConfig()
} }
} }
} }

View file

@ -520,9 +520,6 @@ PageType {
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
parentFlickable: a
clickedFunc: function(){ clickedFunc: function(){
if (clientNameTextField.textField.text !== "") { if (clientNameTextField.textField.text !== "") {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)

View file

@ -1,156 +1,171 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
import ContainerProps 1.0 import ContainerProps 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Components" import "../Components"
import "../Config" import "../Config"
PageType { PageType {
id: root id: root
BackButtonType { BackButtonType {
id: backButton id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
}
onFocusChanged: {
FlickableType { if (this.activeFocus) {
anchors.top: backButton.bottom listView.positionViewAtBeginning()
anchors.bottom: parent.bottom }
contentHeight: content.height }
}
ColumnLayout {
id: content ListViewType {
id: listView
anchors.top: parent.top
anchors.left: parent.left anchors.top: backButton.bottom
anchors.right: parent.right anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 16 anchors.left: parent.left
anchors.leftMargin: 16
header: ColumnLayout {
spacing: 0 width: listView.width
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Full access to the server and VPN") Layout.topMargin: 24
}
headerText: qsTr("Full access to the server and VPN")
ParagraphTextType { }
Layout.fillWidth: true
Layout.topMargin: 24 ParagraphTextType {
Layout.bottomMargin: 24 Layout.fillWidth: true
Layout.leftMargin: 16
text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") + Layout.rightMargin: 16
qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ") Layout.topMargin: 24
color: AmneziaStyle.color.mutedGray Layout.bottomMargin: 24
}
text: qsTr("We recommend that you use full access to the server only for your own additional devices.\n") +
DropDownType { qsTr("If you share full access with other people, they can remove and add protocols and services to the server, which will cause the VPN to work incorrectly for all users. ")
id: serverSelector color: AmneziaStyle.color.mutedGray
}
signal severSelectorIndexChanged
property int currentIndex: 0 DropDownType {
id: serverSelector
Layout.fillWidth: true
Layout.topMargin: 16 signal severSelectorIndexChanged
property int currentIndex: 0
drawerHeight: 0.4375
drawerParent: root Layout.fillWidth: true
Layout.leftMargin: 16
descriptionText: qsTr("Server") Layout.rightMargin: 16
headerText: qsTr("Server") Layout.topMargin: 16
listView: ListViewWithRadioButtonType { drawerHeight: 0.4375
id: serverSelectorListView drawerParent: root
rootWidth: root.width descriptionText: qsTr("Server")
imageSource: "qrc:/images/controls/check.svg" headerText: qsTr("Server")
model: SortFilterProxyModel { listView: ListViewWithRadioButtonType {
id: proxyServersModel id: serverSelectorListView
sourceModel: ServersModel
filters: [ rootWidth: root.width
ValueFilter { imageSource: "qrc:/images/controls/check.svg"
roleName: "hasWriteAccess"
value: true model: SortFilterProxyModel {
} id: proxyServersModel
] sourceModel: ServersModel
} filters: [
ValueFilter {
clickedFunction: function() { roleName: "hasWriteAccess"
handler() value: true
}
if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) { ]
serverSelector.currentIndex = serverSelectorListView.currentIndex }
}
clickedFunction: function() {
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text handler()
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.closeTriggered() if (serverSelector.currentIndex !== serverSelectorListView.currentIndex) {
} serverSelector.currentIndex = serverSelectorListView.currentIndex
}
Component.onCompleted: {
serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ? shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0 shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelectorListView.triggerCurrentItem() serverSelector.closeTriggered()
} }
function handler() { Component.onCompleted: {
serverSelector.text = selectedText serverSelectorListView.currentIndex = ServersModel.isDefaultServerHasWriteAccess() ?
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex) proxyServersModel.mapFromSource(ServersModel.defaultIndex) : 0
} serverSelectorListView.triggerCurrentItem()
} }
}
function handler() {
BasicButtonType { serverSelector.text = selectedText
id: shareButton ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
Layout.fillWidth: true }
Layout.topMargin: 40 }
}
text: qsTr("Share") }
leftImageSource: "qrc:/images/controls/share-2.svg"
model: 1 // fake model to force the ListView to be created without a model
clickedFunc: function() { spacing: 0
PageController.showBusyIndicator(true)
delegate: ColumnLayout {
if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) { width: listView.width
PageController.showBusyIndicator(false)
ExportController.exportErrorOccurred(qsTr("Access error!")) BasicButtonType {
return id: shareButton
} else { Layout.fillWidth: true
ExportController.generateFullAccessConfig() Layout.topMargin: 32
} Layout.leftMargin: 16
Layout.rightMargin: 16
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg"
shareConnectionDrawer.openTriggered()
clickedFunc: function() {
PageController.showBusyIndicator(false) PageController.showBusyIndicator(true)
}
} if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) {
} PageController.showBusyIndicator(false)
} ExportController.exportErrorOccurred(qsTr("Access error!"))
return
ShareConnectionDrawer { } else {
id: shareConnectionDrawer ExportController.generateFullAccessConfig()
}
anchors.fill: parent
} shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
} shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.openTriggered()
PageController.showBusyIndicator(false)
}
}
}
}
ShareConnectionDrawer {
id: shareConnectionDrawer
anchors.fill: parent
}
}