added settings page for v2ray
This commit is contained in:
parent
68830021d6
commit
2b0ba2aff9
9 changed files with 176 additions and 38 deletions
|
|
@ -21,20 +21,24 @@ QString V2RayConfigurator::genV2RayConfig(const ServerCredentials &credentials,
|
||||||
{
|
{
|
||||||
ErrorCode e = ErrorCode::NoError;
|
ErrorCode e = ErrorCode::NoError;
|
||||||
|
|
||||||
QString v2rayVmessClientUuid = m_serverController->getTextFileFromContainer(container, credentials,
|
QString v2RayVmessClientUuid = m_serverController->getTextFileFromContainer(container, credentials,
|
||||||
amnezia::protocols::v2ray::v2rayKeyPath, &e);
|
amnezia::protocols::v2ray::v2rayKeyPath, &e);
|
||||||
v2rayVmessClientUuid.replace("\n", "");
|
if (v2RayVmessClientUuid.isEmpty()) {
|
||||||
|
e = ErrorCode::V2RayKeyMissing;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
v2RayVmessClientUuid.replace("\n", "");
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
if (errorCode) *errorCode = e;
|
if (errorCode) *errorCode = e;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString v2RayClientConfig = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::v2ray_client_template, container),
|
QString v2RayClientConfig = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::v2ray_client_template, container),
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
v2RayClientConfig.replace("$V2RAY_VMESS_CLIENT_UUID", v2rayVmessClientUuid);
|
v2RayClientConfig.replace("$V2RAY_VMESS_CLIENT_UUID", v2RayVmessClientUuid);
|
||||||
v2RayClientConfig = m_serverController->replaceVars(v2RayClientConfig,
|
v2RayClientConfig = m_serverController->replaceVars(v2RayClientConfig,
|
||||||
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
m_serverController->genVarsForScript(credentials, container, containerConfig));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,9 @@ enum ErrorCode
|
||||||
OpenSslFailed,
|
OpenSslFailed,
|
||||||
OpenVpnExecutableCrashed,
|
OpenVpnExecutableCrashed,
|
||||||
ShadowSocksExecutableCrashed,
|
ShadowSocksExecutableCrashed,
|
||||||
CloakExecutableCrashed
|
CloakExecutableCrashed,
|
||||||
|
V2RayExecutableCrashed,
|
||||||
|
V2RayKeyMissing
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace amnezia
|
} // namespace amnezia
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,12 @@ QString errorString(ErrorCode code){
|
||||||
case (ShadowSocksExecutableMissing): return QObject::tr("ShadowSocks (ss-local) executable missing");
|
case (ShadowSocksExecutableMissing): return QObject::tr("ShadowSocks (ss-local) executable missing");
|
||||||
case (CloakExecutableMissing): return QObject::tr("Cloak (ck-client) executable missing");
|
case (CloakExecutableMissing): return QObject::tr("Cloak (ck-client) executable missing");
|
||||||
case (AmneziaServiceConnectionFailed): return QObject::tr("Amnezia helper service error");
|
case (AmneziaServiceConnectionFailed): return QObject::tr("Amnezia helper service error");
|
||||||
case (V2RayExecutableMissing): return QObject::tr("V2Ray (v2ray) executable missing");
|
|
||||||
case (OpenSslFailed): return QObject::tr("OpenSSL failed");
|
case (OpenSslFailed): return QObject::tr("OpenSSL failed");
|
||||||
|
|
||||||
|
// V2Ray errors
|
||||||
|
case (V2RayExecutableMissing): return QObject::tr("V2Ray (v2ray) executable missing");
|
||||||
|
case (V2RayKeyMissing): return QObject::tr("V2Ray key missing");
|
||||||
|
|
||||||
// VPN errors
|
// VPN errors
|
||||||
case (OpenVpnAdaptersInUseError): return QObject::tr("Can't connect: another VPN connection is active");
|
case (OpenVpnAdaptersInUseError): return QObject::tr("Can't connect: another VPN connection is active");
|
||||||
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
|
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ ErrorCode V2RayProtocol::start()
|
||||||
|
|
||||||
QStringList args = QStringList() << "-c" << m_v2RayConfigFile.fileName();
|
QStringList args = QStringList() << "-c" << m_v2RayConfigFile.fileName();
|
||||||
|
|
||||||
qDebug().noquote() << "ShadowSocksVpnProtocol::start()"
|
qDebug().noquote() << "V2RayProtocol::start()"
|
||||||
<< v2RayExecPath() << args.join(" ");
|
<< v2RayExecPath() << args.join(" ");
|
||||||
|
|
||||||
m_v2RayProcess.setProcessChannelMode(QProcess::MergedChannels);
|
m_v2RayProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
|
|
@ -41,14 +41,14 @@ ErrorCode V2RayProtocol::start()
|
||||||
m_v2RayProcess.setArguments(args);
|
m_v2RayProcess.setArguments(args);
|
||||||
|
|
||||||
connect(&m_v2RayProcess, &QProcess::readyReadStandardOutput, this, [this](){
|
connect(&m_v2RayProcess, &QProcess::readyReadStandardOutput, this, [this](){
|
||||||
qDebug().noquote() << "v2ray:" << m_v2RayProcess.readAllStandardOutput();
|
qDebug().noquote() << "V2Ray:" << m_v2RayProcess.readAllStandardOutput();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(&m_v2RayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
connect(&m_v2RayProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
qDebug().noquote() << "V2RayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
|
qDebug().noquote() << "V2RayProtocol finished, exitCode, exiStatus" << exitCode << exitStatus;
|
||||||
setConnectionState(VpnProtocol::Disconnected);
|
setConnectionState(VpnProtocol::Disconnected);
|
||||||
if (exitStatus != QProcess::NormalExit){
|
if (exitStatus != QProcess::NormalExit){
|
||||||
emit protocolError(amnezia::ErrorCode::ShadowSocksExecutableCrashed); //todo
|
emit protocolError(amnezia::ErrorCode::V2RayExecutableCrashed);
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
if (exitCode != 0 ) {
|
if (exitCode != 0 ) {
|
||||||
|
|
@ -64,8 +64,7 @@ ErrorCode V2RayProtocol::start()
|
||||||
setConnectionState(VpnConnectionState::Connecting);
|
setConnectionState(VpnConnectionState::Connecting);
|
||||||
|
|
||||||
return OpenVpnProtocol::start();
|
return OpenVpnProtocol::start();
|
||||||
}
|
} else return ErrorCode::V2RayExecutableMissing;
|
||||||
else return ErrorCode::ShadowSocksExecutableMissing;
|
|
||||||
#else
|
#else
|
||||||
return ErrorCode::NotImplementedError;
|
return ErrorCode::NotImplementedError;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ dhcp-option DNS $PRIMARY_DNS
|
||||||
dhcp-option DNS $SECONDARY_DNS
|
dhcp-option DNS $SECONDARY_DNS
|
||||||
block-outside-dns
|
block-outside-dns
|
||||||
|
|
||||||
# TODO test next 3 lines for (feature/v2ray-container)
|
|
||||||
socks-proxy 127.0.0.1 $V2RAY_SOCKS_LOCAL_PORT
|
socks-proxy 127.0.0.1 $V2RAY_SOCKS_LOCAL_PORT
|
||||||
route $REMOTE_HOST 255.255.255.255 net_gateway
|
route $REMOTE_HOST 255.255.255.255 net_gateway
|
||||||
remote 127.0.0.1 $OPENVPN_PORT
|
remote 127.0.0.1 $OPENVPN_PORT
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,14 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
|
||||||
|
|
||||||
progressBarFunc.setTextVisibleFunc(true);
|
progressBarFunc.setTextVisibleFunc(true);
|
||||||
progressBarFunc.setTextFunc(QString("Configuring..."));
|
progressBarFunc.setTextFunc(QString("Configuring..."));
|
||||||
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
|
|
||||||
|
auto installAction = [this, containerConfig, &newContainerConfig](){
|
||||||
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
|
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
|
||||||
uiLogic()->selectedDockerContainer,
|
uiLogic()->selectedDockerContainer,
|
||||||
containerConfig,
|
containerConfig,
|
||||||
newContainerConfig);
|
newContainerConfig);
|
||||||
},
|
};
|
||||||
pageFunc, progressBarFunc,
|
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
|
||||||
saveButtonFunc, waitInfoFunc,
|
saveButtonFunc, waitInfoFunc,
|
||||||
busyInfoFuncy, cancelButtonFunc);
|
busyInfoFuncy, cancelButtonFunc);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ using namespace PageEnumNS;
|
||||||
|
|
||||||
V2RayLogic::V2RayLogic(UiLogic *logic, QObject *parent):
|
V2RayLogic::V2RayLogic(UiLogic *logic, QObject *parent):
|
||||||
PageProtocolLogicBase(logic, parent),
|
PageProtocolLogicBase(logic, parent),
|
||||||
m_comboBoxCipherText{"chacha20-poly1305"},
|
|
||||||
m_lineEditPortText{},
|
m_lineEditPortText{},
|
||||||
m_pushButtonSaveVisible{false},
|
m_pushButtonSaveVisible{false},
|
||||||
m_progressBarResetVisible{false},
|
m_progressBarResetVisible{false},
|
||||||
|
|
@ -24,36 +23,31 @@ V2RayLogic::V2RayLogic(UiLogic *logic, QObject *parent):
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void V2RayLogic::updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData)
|
void V2RayLogic::updateProtocolPage(const QJsonObject &v2RayConfig, DockerContainer container, bool haveAuthData)
|
||||||
{
|
{
|
||||||
set_pageEnabled(haveAuthData);
|
set_pageEnabled(haveAuthData);
|
||||||
set_pushButtonSaveVisible(haveAuthData);
|
set_pushButtonSaveVisible(haveAuthData);
|
||||||
set_progressBarResetVisible(haveAuthData);
|
set_progressBarResetVisible(haveAuthData);
|
||||||
|
|
||||||
set_comboBoxCipherText(ssConfig.value(config_key::cipher).
|
set_lineEditPortText(v2RayConfig.value(config_key::port).
|
||||||
toString(protocols::shadowsocks::defaultCipher));
|
toString(protocols::v2ray::defaultServerPort));
|
||||||
|
|
||||||
set_lineEditPortText(ssConfig.value(config_key::port).
|
set_lineEditPortEnabled(container == DockerContainer::V2Ray);
|
||||||
toString(protocols::shadowsocks::defaultPort));
|
|
||||||
|
|
||||||
set_lineEditPortEnabled(container == DockerContainer::ShadowSocks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject V2RayLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
|
QJsonObject V2RayLogic::getProtocolConfigFromPage(QJsonObject oldConfig)
|
||||||
{
|
{
|
||||||
oldConfig.insert(config_key::cipher, comboBoxCipherText());
|
|
||||||
oldConfig.insert(config_key::port, lineEditPortText());
|
oldConfig.insert(config_key::port, lineEditPortText());
|
||||||
|
|
||||||
return oldConfig;
|
return oldConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V2RayLogic::onPushButtonSaveClicked()
|
void V2RayLogic::onPushButtonSaveClicked()
|
||||||
{
|
{
|
||||||
QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::ShadowSocks);
|
QJsonObject protocolConfig = m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, Proto::V2Ray);
|
||||||
|
|
||||||
QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer);
|
QJsonObject containerConfig = m_settings->containerConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer);
|
||||||
QJsonObject newContainerConfig = containerConfig;
|
QJsonObject newContainerConfig = containerConfig;
|
||||||
newContainerConfig.insert(ProtocolProps::protoToString(Proto::ShadowSocks), protocolConfig);
|
newContainerConfig.insert(ProtocolProps::protoToString(Proto::V2Ray), protocolConfig);
|
||||||
ServerConfiguringProgressLogic::PageFunc pageFunc;
|
ServerConfiguringProgressLogic::PageFunc pageFunc;
|
||||||
pageFunc.setEnabledFunc = [this] (bool enabled) -> void {
|
pageFunc.setEnabledFunc = [this] (bool enabled) -> void {
|
||||||
set_pageEnabled(enabled);
|
set_pageEnabled(enabled);
|
||||||
|
|
@ -104,13 +98,15 @@ void V2RayLogic::onPushButtonSaveClicked()
|
||||||
|
|
||||||
progressBarFunc.setTextVisibleFunc(true);
|
progressBarFunc.setTextVisibleFunc(true);
|
||||||
progressBarFunc.setTextFunc(QString("Configuring..."));
|
progressBarFunc.setTextFunc(QString("Configuring..."));
|
||||||
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
|
|
||||||
|
auto installAction = [this, containerConfig, &newContainerConfig]() {
|
||||||
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
|
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->selectedServerIndex),
|
||||||
uiLogic()->selectedDockerContainer,
|
uiLogic()->selectedDockerContainer,
|
||||||
containerConfig,
|
containerConfig,
|
||||||
newContainerConfig);
|
newContainerConfig);
|
||||||
},
|
};
|
||||||
pageFunc, progressBarFunc,
|
|
||||||
|
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
|
||||||
saveButtonFunc, waitInfoFunc,
|
saveButtonFunc, waitInfoFunc,
|
||||||
busyInfoFuncy, cancelButtonFunc);
|
busyInfoFuncy, cancelButtonFunc);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,22 @@ class V2RayLogic : public PageProtocolLogicBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
AUTO_PROPERTY(QString, comboBoxCipherText)
|
|
||||||
AUTO_PROPERTY(QString, lineEditPortText)
|
|
||||||
AUTO_PROPERTY(bool, pushButtonSaveVisible)
|
|
||||||
AUTO_PROPERTY(bool, progressBarResetVisible)
|
|
||||||
AUTO_PROPERTY(bool, lineEditPortEnabled)
|
AUTO_PROPERTY(bool, lineEditPortEnabled)
|
||||||
|
AUTO_PROPERTY(QString, lineEditPortText)
|
||||||
|
|
||||||
AUTO_PROPERTY(bool, labelInfoVisible)
|
AUTO_PROPERTY(bool, labelInfoVisible)
|
||||||
AUTO_PROPERTY(QString, labelInfoText)
|
AUTO_PROPERTY(QString, labelInfoText)
|
||||||
|
|
||||||
AUTO_PROPERTY(int, progressBarResetValue)
|
AUTO_PROPERTY(int, progressBarResetValue)
|
||||||
AUTO_PROPERTY(int, progressBarResetMaximium)
|
AUTO_PROPERTY(int, progressBarResetMaximium)
|
||||||
|
AUTO_PROPERTY(bool, progressBarResetVisible)
|
||||||
AUTO_PROPERTY(bool, progressBarTextVisible)
|
AUTO_PROPERTY(bool, progressBarTextVisible)
|
||||||
AUTO_PROPERTY(QString, progressBarText)
|
AUTO_PROPERTY(QString, progressBarText)
|
||||||
|
|
||||||
AUTO_PROPERTY(bool, labelServerBusyVisible)
|
AUTO_PROPERTY(bool, labelServerBusyVisible)
|
||||||
AUTO_PROPERTY(QString, labelServerBusyText)
|
AUTO_PROPERTY(QString, labelServerBusyText)
|
||||||
|
|
||||||
|
AUTO_PROPERTY(bool, pushButtonSaveVisible)
|
||||||
AUTO_PROPERTY(bool, pushButtonCancelVisible)
|
AUTO_PROPERTY(bool, pushButtonCancelVisible)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -34,7 +35,7 @@ public:
|
||||||
explicit V2RayLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
explicit V2RayLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||||
~V2RayLogic() = default;
|
~V2RayLogic() = default;
|
||||||
|
|
||||||
void updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData) override;
|
void updateProtocolPage(const QJsonObject &v2rayConfig, DockerContainer container, bool haveAuthData) override;
|
||||||
QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override;
|
QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,138 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ProtocolEnum 1.0
|
||||||
|
import "../"
|
||||||
|
import "../../Controls"
|
||||||
|
import "../../Config"
|
||||||
|
|
||||||
|
PageProtocolBase {
|
||||||
|
id: root
|
||||||
|
protocol: ProtocolEnum.V2Ray
|
||||||
|
logic: UiLogic.protocolLogic(protocol)
|
||||||
|
|
||||||
|
BackButton {
|
||||||
|
id: back
|
||||||
|
enabled: !logic.pushButtonCancelVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
Caption {
|
||||||
|
id: caption
|
||||||
|
text: qsTr("V2Ray Settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
enabled: logic.pageEnabled
|
||||||
|
anchors.top: caption.bottom
|
||||||
|
anchors.left: root.left
|
||||||
|
anchors.right: root.right
|
||||||
|
anchors.bottom: pb_save.top
|
||||||
|
anchors.margins: 20
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
Layout.preferredWidth: 0.3 * root.width - 10
|
||||||
|
height: 31
|
||||||
|
text: qsTr("Port")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 31
|
||||||
|
text: logic.lineEditPortText
|
||||||
|
onEditingFinished: {
|
||||||
|
logic.lineEditPortText = text
|
||||||
|
}
|
||||||
|
enabled: logic.lineEditPortEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: logic.labelServerBusyVisible
|
||||||
|
text: logic.labelServerBusyText
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: logic.labelInfoVisible
|
||||||
|
text: logic.labelInfoText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar {
|
||||||
|
id: progressBar_reset
|
||||||
|
anchors.fill: pb_save
|
||||||
|
from: 0
|
||||||
|
to: logic.progressBarResetMaximium
|
||||||
|
value: logic.progressBarResetValue
|
||||||
|
visible: logic.progressBarResetVisible
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: parent.width
|
||||||
|
implicitHeight: parent.height
|
||||||
|
color: "#100A44"
|
||||||
|
radius: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: parent.width
|
||||||
|
implicitHeight: parent.height
|
||||||
|
Rectangle {
|
||||||
|
width: progressBar_reset.visualPosition * parent.width
|
||||||
|
height: parent.height
|
||||||
|
radius: 4
|
||||||
|
color: Qt.rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LabelType {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: logic.progressBarText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.family: "Lato"
|
||||||
|
font.styleName: "normal"
|
||||||
|
font.pixelSize: 16
|
||||||
|
color: "#D4D4D4"
|
||||||
|
visible: logic.progressBarTextVisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
id: pb_save
|
||||||
|
enabled: logic.pageEnabled
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: root.bottom
|
||||||
|
anchors.bottomMargin: 20
|
||||||
|
width: root.width - 60
|
||||||
|
height: 40
|
||||||
|
text: qsTr("Save and restart VPN")
|
||||||
|
visible: logic.pushButtonSaveVisible
|
||||||
|
onClicked: {
|
||||||
|
logic.onPushButtonSaveClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueButtonType {
|
||||||
|
anchors.fill: pb_save
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
visible: logic.pushButtonCancelVisible
|
||||||
|
enabled: logic.pushButtonCancelVisible
|
||||||
|
onClicked: {
|
||||||
|
logic.onPushButtonCancelClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue