840 lines
34 KiB
QML
840 lines
34 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import ".."
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import Caelestia.Config
|
|
import qs.components
|
|
import qs.components.controls
|
|
import qs.components.effects
|
|
import qs.services
|
|
|
|
ColumnLayout {
|
|
id: root
|
|
|
|
required property Session session
|
|
property bool showHeader: true
|
|
property int pendingSwitchIndex: -1
|
|
|
|
spacing: Tokens.spacing.normal
|
|
|
|
Connections {
|
|
function onConnectedChanged() {
|
|
if (!VPN.connected && root.pendingSwitchIndex >= 0) {
|
|
const targetIndex = root.pendingSwitchIndex;
|
|
root.pendingSwitchIndex = -1;
|
|
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
const p = Config.utilities.vpn.provider[i];
|
|
if (typeof p === "object") {
|
|
const newProvider = {
|
|
name: p.name,
|
|
displayName: p.displayName,
|
|
interface: p.interface,
|
|
enabled: (i === targetIndex)
|
|
};
|
|
if (p.connectCmd && p.connectCmd.length > 0) {
|
|
newProvider.connectCmd = p.connectCmd;
|
|
}
|
|
if (p.disconnectCmd && p.disconnectCmd.length > 0) {
|
|
newProvider.disconnectCmd = p.disconnectCmd;
|
|
}
|
|
providers.push(newProvider);
|
|
} else {
|
|
providers.push(p);
|
|
}
|
|
}
|
|
Config.utilities.vpn.provider = providers;
|
|
|
|
Qt.callLater(function () {
|
|
VPN.toggle();
|
|
});
|
|
}
|
|
}
|
|
|
|
target: VPN
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("+ Add VPN Provider")
|
|
inactiveColour: Colours.palette.m3primaryContainer
|
|
inactiveOnColour: Colours.palette.m3onPrimaryContainer
|
|
|
|
onClicked: {
|
|
vpnDialog.showProviderSelection();
|
|
}
|
|
}
|
|
|
|
ListView {
|
|
id: listView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: contentHeight
|
|
|
|
interactive: false
|
|
spacing: Tokens.spacing.smaller
|
|
|
|
model: ScriptModel {
|
|
values: Config.utilities.vpn.provider.map((provider, index) => {
|
|
const isObject = typeof provider === "object";
|
|
const name = isObject ? (provider.name || "custom") : String(provider);
|
|
const displayName = isObject ? (provider.displayName || name) : name;
|
|
const iface = isObject ? (provider.interface || "") : "";
|
|
const enabled = isObject ? (provider.enabled === true) : false;
|
|
|
|
return {
|
|
index: index,
|
|
name: name,
|
|
displayName: displayName,
|
|
interface: iface,
|
|
provider: provider,
|
|
enabled: enabled
|
|
};
|
|
})
|
|
}
|
|
|
|
delegate: Component {
|
|
StyledRect {
|
|
required property var modelData
|
|
required property int index
|
|
|
|
width: ListView.view ? ListView.view.width : undefined
|
|
implicitHeight: rowLayout.implicitHeight + Tokens.padding.normal * 2
|
|
|
|
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (root.session && root.session.vpn && root.session.vpn.active === modelData) ? Colours.tPalette.m3surfaceContainer.a : 0)
|
|
radius: Tokens.rounding.normal
|
|
|
|
StateLayer {
|
|
function onClicked(): void {
|
|
if (root.session && root.session.vpn) {
|
|
root.session.vpn.active = modelData;
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
id: rowLayout
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.margins: Tokens.padding.normal
|
|
|
|
spacing: Tokens.spacing.normal
|
|
|
|
StyledRect {
|
|
implicitWidth: implicitHeight
|
|
implicitHeight: icon.implicitHeight + Tokens.padding.normal * 2
|
|
|
|
radius: Tokens.rounding.normal
|
|
color: modelData.enabled && VPN.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh
|
|
|
|
MaterialIcon {
|
|
id: icon
|
|
|
|
anchors.centerIn: parent
|
|
text: modelData.enabled && VPN.connected ? "vpn_key" : "vpn_key_off"
|
|
font.pointSize: Tokens.font.size.large
|
|
fill: modelData.enabled && VPN.connected ? 1 : 0
|
|
color: modelData.enabled && VPN.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
|
|
spacing: 0
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideRight
|
|
maximumLineCount: 1
|
|
|
|
text: modelData.displayName || qsTr("Unknown")
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.smaller
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: {
|
|
if (!modelData.enabled)
|
|
return qsTr("Disabled");
|
|
|
|
if (VPN.connecting)
|
|
return qsTr("Connecting...");
|
|
|
|
switch (VPN.status.state) {
|
|
case "connected":
|
|
return qsTr("Connected");
|
|
case "disconnected":
|
|
return qsTr("Enabled");
|
|
case "connecting":
|
|
return qsTr("Connecting...");
|
|
case "needs-auth":
|
|
return qsTr("Auth required");
|
|
case "error":
|
|
return qsTr("Error");
|
|
default:
|
|
return qsTr("Enabled");
|
|
}
|
|
}
|
|
color: {
|
|
if (!modelData.enabled)
|
|
return Colours.palette.m3outline;
|
|
if (VPN.status.state === "connected")
|
|
return Colours.palette.m3primary;
|
|
if (VPN.status.state === "error")
|
|
return Colours.palette.m3error;
|
|
if (VPN.status.state === "needs-auth")
|
|
return Colours.palette.m3tertiary;
|
|
return Colours.palette.m3onSurface;
|
|
}
|
|
font.pointSize: Tokens.font.size.small
|
|
font.weight: modelData.enabled && VPN.connected ? 500 : 400
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
implicitWidth: implicitHeight
|
|
implicitHeight: connectIcon.implicitHeight + Tokens.padding.smaller * 2
|
|
|
|
radius: Tokens.rounding.full
|
|
color: Qt.alpha(Colours.palette.m3primaryContainer, VPN.connected && modelData.enabled ? 1 : 0)
|
|
|
|
StateLayer {
|
|
function onClicked(): void {
|
|
const clickedIndex = modelData.index;
|
|
|
|
if (modelData.enabled) {
|
|
VPN.toggle();
|
|
} else {
|
|
if (VPN.connected) {
|
|
root.pendingSwitchIndex = clickedIndex;
|
|
VPN.toggle();
|
|
} else {
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
const p = Config.utilities.vpn.provider[i];
|
|
if (typeof p === "object") {
|
|
const newProvider = {
|
|
name: p.name,
|
|
displayName: p.displayName,
|
|
interface: p.interface,
|
|
enabled: (i === clickedIndex)
|
|
};
|
|
if (p.connectCmd && p.connectCmd.length > 0) {
|
|
newProvider.connectCmd = p.connectCmd;
|
|
}
|
|
if (p.disconnectCmd && p.disconnectCmd.length > 0) {
|
|
newProvider.disconnectCmd = p.disconnectCmd;
|
|
}
|
|
providers.push(newProvider);
|
|
} else {
|
|
providers.push(p);
|
|
}
|
|
}
|
|
Config.utilities.vpn.provider = providers;
|
|
|
|
Qt.callLater(function () {
|
|
VPN.toggle();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
enabled: !VPN.connecting
|
|
}
|
|
|
|
MaterialIcon {
|
|
id: connectIcon
|
|
|
|
anchors.centerIn: parent
|
|
text: VPN.connected && modelData.enabled ? "link_off" : "link"
|
|
color: VPN.connected && modelData.enabled ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
|
|
}
|
|
}
|
|
|
|
StyledRect {
|
|
implicitWidth: implicitHeight
|
|
implicitHeight: deleteIcon.implicitHeight + Tokens.padding.smaller * 2
|
|
|
|
radius: Tokens.rounding.full
|
|
color: "transparent"
|
|
|
|
StateLayer {
|
|
function onClicked(): void {
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
if (i !== modelData.index) {
|
|
const p = Config.utilities.vpn.provider[i];
|
|
const reconstructed = {
|
|
name: p.name,
|
|
displayName: p.displayName,
|
|
interface: p.interface,
|
|
enabled: p.enabled
|
|
};
|
|
if (p.connectCmd && p.connectCmd.length > 0) {
|
|
reconstructed.connectCmd = p.connectCmd;
|
|
}
|
|
if (p.disconnectCmd && p.disconnectCmd.length > 0) {
|
|
reconstructed.disconnectCmd = p.disconnectCmd;
|
|
}
|
|
providers.push(reconstructed);
|
|
}
|
|
}
|
|
Config.utilities.vpn.provider = providers;
|
|
}
|
|
}
|
|
|
|
MaterialIcon {
|
|
id: deleteIcon
|
|
|
|
anchors.centerIn: parent
|
|
text: "delete"
|
|
color: Colours.palette.m3onSurface
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Popup {
|
|
id: vpnDialog
|
|
|
|
property string currentState: "selection"
|
|
property int editIndex: -1
|
|
property string providerName: ""
|
|
property string displayName: ""
|
|
property string interfaceName: ""
|
|
property string connectCmd: ""
|
|
property string disconnectCmd: ""
|
|
|
|
function showProviderSelection(): void {
|
|
currentState = "selection";
|
|
open();
|
|
}
|
|
|
|
function closeWithAnimation(): void {
|
|
close();
|
|
}
|
|
|
|
function showAddForm(providerType: string, defaultDisplayName: string): void {
|
|
editIndex = -1;
|
|
providerName = providerType;
|
|
displayName = defaultDisplayName;
|
|
interfaceName = "";
|
|
connectCmd = "";
|
|
disconnectCmd = "";
|
|
|
|
if (currentState === "selection") {
|
|
transitionToForm.start();
|
|
} else {
|
|
currentState = "form";
|
|
isClosing = false;
|
|
open();
|
|
}
|
|
}
|
|
|
|
function showEditForm(index: int): void {
|
|
const provider = Config.utilities.vpn.provider[index];
|
|
const isObject = typeof provider === "object";
|
|
|
|
editIndex = index;
|
|
providerName = isObject ? (provider.name || "custom") : String(provider);
|
|
displayName = isObject ? (provider.displayName || providerName) : providerName;
|
|
interfaceName = isObject ? (provider.interface || "") : "";
|
|
connectCmd = isObject && provider.connectCmd ? provider.connectCmd.join(" ") : "";
|
|
disconnectCmd = isObject && provider.disconnectCmd ? provider.disconnectCmd.join(" ") : "";
|
|
|
|
currentState = "form";
|
|
open();
|
|
}
|
|
|
|
parent: Overlay.overlay
|
|
x: Math.round((parent.width - width) / 2)
|
|
y: Math.round((parent.height - height) / 2)
|
|
implicitWidth: Math.min(400, parent.width - Tokens.padding.large * 2)
|
|
padding: Tokens.padding.large * 1.5
|
|
|
|
modal: true
|
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
|
|
|
opacity: 0
|
|
scale: 0.7
|
|
|
|
enter: Transition {
|
|
ParallelAnimation {
|
|
Anim {
|
|
property: "opacity"
|
|
from: 0
|
|
to: 1
|
|
duration: Tokens.anim.durations.normal
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
Anim {
|
|
property: "scale"
|
|
from: 0.7
|
|
to: 1
|
|
duration: Tokens.anim.durations.normal
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
}
|
|
|
|
exit: Transition {
|
|
ParallelAnimation {
|
|
Anim {
|
|
property: "opacity"
|
|
from: 1
|
|
to: 0
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
Anim {
|
|
property: "scale"
|
|
from: 1
|
|
to: 0.7
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
}
|
|
|
|
Overlay.modal: Rectangle {
|
|
color: Qt.rgba(0, 0, 0, 0.4 * vpnDialog.opacity)
|
|
}
|
|
|
|
onClosed: {
|
|
currentState = "selection";
|
|
}
|
|
|
|
background: StyledRect {
|
|
color: Colours.palette.m3surfaceContainerHigh
|
|
radius: Tokens.rounding.large
|
|
|
|
Elevation {
|
|
anchors.fill: parent
|
|
radius: parent.radius
|
|
level: 3
|
|
z: -1
|
|
}
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {
|
|
duration: Tokens.anim.durations.normal
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
}
|
|
|
|
contentItem: Item {
|
|
implicitHeight: vpnDialog.currentState === "selection" ? selectionContent.implicitHeight : formContent.implicitHeight
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {
|
|
duration: Tokens.anim.durations.normal
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: selectionContent
|
|
|
|
anchors.fill: parent
|
|
spacing: Tokens.spacing.normal
|
|
visible: vpnDialog.currentState === "selection"
|
|
opacity: vpnDialog.currentState === "selection" ? 1 : 0
|
|
|
|
Behavior on opacity {
|
|
Anim {
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: qsTr("Add VPN Provider")
|
|
font.pointSize: Tokens.font.size.large
|
|
font.weight: 500
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Choose a provider to add")
|
|
wrapMode: Text.WordWrap
|
|
color: Colours.palette.m3outline
|
|
font.pointSize: Tokens.font.size.small
|
|
}
|
|
|
|
TextButton {
|
|
Layout.topMargin: Tokens.spacing.normal
|
|
Layout.fillWidth: true
|
|
text: qsTr("NetBird")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: {
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
providers.push(Config.utilities.vpn.provider[i]);
|
|
}
|
|
providers.push({
|
|
name: "netbird",
|
|
displayName: "NetBird",
|
|
interface: "wt0"
|
|
});
|
|
Config.utilities.vpn.provider = providers;
|
|
vpnDialog.closeWithAnimation();
|
|
}
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Tailscale")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: {
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
providers.push(Config.utilities.vpn.provider[i]);
|
|
}
|
|
providers.push({
|
|
name: "tailscale",
|
|
displayName: "Tailscale",
|
|
interface: "tailscale0"
|
|
});
|
|
Config.utilities.vpn.provider = providers;
|
|
vpnDialog.closeWithAnimation();
|
|
}
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Cloudflare WARP")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: {
|
|
const providers = [];
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
providers.push(Config.utilities.vpn.provider[i]);
|
|
}
|
|
providers.push({
|
|
name: "warp",
|
|
displayName: "Cloudflare WARP",
|
|
interface: "CloudflareWARP"
|
|
});
|
|
Config.utilities.vpn.provider = providers;
|
|
vpnDialog.closeWithAnimation();
|
|
}
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("WireGuard")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: {
|
|
vpnDialog.showAddForm("wireguard", "WireGuard");
|
|
}
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Custom")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: {
|
|
vpnDialog.showAddForm("custom", "Custom VPN");
|
|
}
|
|
}
|
|
|
|
TextButton {
|
|
Layout.topMargin: Tokens.spacing.normal
|
|
Layout.fillWidth: true
|
|
text: qsTr("Cancel")
|
|
inactiveColour: Colours.palette.m3secondaryContainer
|
|
inactiveOnColour: Colours.palette.m3onSecondaryContainer
|
|
onClicked: vpnDialog.closeWithAnimation()
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: formContent
|
|
|
|
anchors.fill: parent
|
|
spacing: Tokens.spacing.normal
|
|
visible: vpnDialog.currentState === "form"
|
|
opacity: vpnDialog.currentState === "form" ? 1 : 0
|
|
|
|
Behavior on opacity {
|
|
Anim {
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: vpnDialog.editIndex >= 0 ? qsTr("Edit VPN Provider") : qsTr("Add %1 VPN").arg(vpnDialog.displayName)
|
|
font.pointSize: Tokens.font.size.large
|
|
font.weight: 500
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.smaller / 2
|
|
|
|
StyledText {
|
|
text: qsTr("Display Name")
|
|
font.pointSize: Tokens.font.size.small
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: 40
|
|
color: displayNameField.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
radius: Tokens.rounding.small
|
|
border.width: 1
|
|
border.color: displayNameField.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3)
|
|
|
|
Behavior on color {
|
|
CAnim {}
|
|
}
|
|
Behavior on border.color {
|
|
CAnim {}
|
|
}
|
|
|
|
StyledTextField {
|
|
id: displayNameField
|
|
|
|
anchors.centerIn: parent
|
|
width: parent.width - Tokens.padding.normal
|
|
horizontalAlignment: TextInput.AlignLeft
|
|
text: vpnDialog.displayName
|
|
onTextChanged: vpnDialog.displayName = text
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.smaller / 2
|
|
|
|
StyledText {
|
|
text: qsTr("Interface (e.g., wg0, torguard)")
|
|
font.pointSize: Tokens.font.size.small
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: 40
|
|
color: interfaceNameField.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
radius: Tokens.rounding.small
|
|
border.width: 1
|
|
border.color: interfaceNameField.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3)
|
|
|
|
Behavior on color {
|
|
CAnim {}
|
|
}
|
|
Behavior on border.color {
|
|
CAnim {}
|
|
}
|
|
|
|
StyledTextField {
|
|
id: interfaceNameField
|
|
|
|
anchors.centerIn: parent
|
|
width: parent.width - Tokens.padding.normal
|
|
horizontalAlignment: TextInput.AlignLeft
|
|
text: vpnDialog.interfaceName
|
|
onTextChanged: vpnDialog.interfaceName = text
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.smaller / 2
|
|
visible: vpnDialog.editIndex >= 0 ? (vpnDialog.connectCmd.length > 0) : (vpnDialog.providerName === "custom")
|
|
|
|
StyledText {
|
|
text: qsTr("Connect Command (e.g., wg-quick up wg0)")
|
|
font.pointSize: Tokens.font.size.small
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: 40
|
|
color: connectCmdField.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
radius: Tokens.rounding.small
|
|
border.width: 1
|
|
border.color: connectCmdField.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3)
|
|
|
|
Behavior on color {
|
|
CAnim {}
|
|
}
|
|
Behavior on border.color {
|
|
CAnim {}
|
|
}
|
|
|
|
StyledTextField {
|
|
id: connectCmdField
|
|
|
|
anchors.centerIn: parent
|
|
width: parent.width - Tokens.padding.normal
|
|
horizontalAlignment: TextInput.AlignLeft
|
|
text: vpnDialog.connectCmd
|
|
onTextChanged: vpnDialog.connectCmd = text
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.smaller / 2
|
|
visible: vpnDialog.editIndex >= 0 ? (vpnDialog.connectCmd.length > 0) : (vpnDialog.providerName === "custom")
|
|
|
|
StyledText {
|
|
text: qsTr("Disconnect Command (e.g., wg-quick down wg0)")
|
|
font.pointSize: Tokens.font.size.small
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
}
|
|
|
|
StyledRect {
|
|
Layout.fillWidth: true
|
|
implicitHeight: 40
|
|
color: disconnectCmdField.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
|
radius: Tokens.rounding.small
|
|
border.width: 1
|
|
border.color: disconnectCmdField.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3)
|
|
|
|
Behavior on color {
|
|
CAnim {}
|
|
}
|
|
Behavior on border.color {
|
|
CAnim {}
|
|
}
|
|
|
|
StyledTextField {
|
|
id: disconnectCmdField
|
|
|
|
anchors.centerIn: parent
|
|
width: parent.width - Tokens.padding.normal
|
|
horizontalAlignment: TextInput.AlignLeft
|
|
text: vpnDialog.disconnectCmd
|
|
onTextChanged: vpnDialog.disconnectCmd = text
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.topMargin: Tokens.spacing.normal
|
|
Layout.fillWidth: true
|
|
spacing: Tokens.spacing.normal
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Cancel")
|
|
inactiveColour: Colours.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColour: Colours.palette.m3onSurface
|
|
onClicked: vpnDialog.closeWithAnimation()
|
|
}
|
|
|
|
TextButton {
|
|
Layout.fillWidth: true
|
|
text: qsTr("Save")
|
|
enabled: {
|
|
const hasCommands = vpnDialog.connectCmd.length > 0 || vpnDialog.disconnectCmd.length > 0;
|
|
if (hasCommands) {
|
|
return vpnDialog.interfaceName.length > 0 && vpnDialog.connectCmd.length > 0 && vpnDialog.disconnectCmd.length > 0;
|
|
}
|
|
return vpnDialog.interfaceName.length > 0;
|
|
}
|
|
inactiveColour: Colours.palette.m3primaryContainer
|
|
inactiveOnColour: Colours.palette.m3onPrimaryContainer
|
|
|
|
onClicked: {
|
|
const providers = [];
|
|
const hasCommands = vpnDialog.connectCmd.length > 0 && vpnDialog.disconnectCmd.length > 0;
|
|
const newProvider = {
|
|
displayName: vpnDialog.displayName || vpnDialog.interfaceName,
|
|
enabled: false,
|
|
interface: vpnDialog.interfaceName,
|
|
name: vpnDialog.providerName
|
|
};
|
|
|
|
if (hasCommands) {
|
|
newProvider.connectCmd = vpnDialog.connectCmd.split(" ").filter(s => s.length > 0);
|
|
newProvider.disconnectCmd = vpnDialog.disconnectCmd.split(" ").filter(s => s.length > 0);
|
|
}
|
|
|
|
if (vpnDialog.editIndex >= 0) {
|
|
const oldProvider = Config.utilities.vpn.provider[vpnDialog.editIndex];
|
|
if (typeof oldProvider === "object" && oldProvider.enabled !== undefined) {
|
|
newProvider.enabled = oldProvider.enabled;
|
|
}
|
|
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
if (i === vpnDialog.editIndex) {
|
|
providers.push(newProvider);
|
|
} else {
|
|
providers.push(Config.utilities.vpn.provider[i]);
|
|
}
|
|
}
|
|
} else {
|
|
for (let i = 0; i < Config.utilities.vpn.provider.length; i++) {
|
|
providers.push(Config.utilities.vpn.provider[i]);
|
|
}
|
|
providers.push(newProvider);
|
|
}
|
|
|
|
Config.utilities.vpn.provider = providers;
|
|
vpnDialog.closeWithAnimation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SequentialAnimation {
|
|
id: transitionToForm
|
|
|
|
ParallelAnimation {
|
|
Anim {
|
|
target: selectionContent
|
|
property: "opacity"
|
|
to: 0
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
|
|
ScriptAction {
|
|
script: {
|
|
vpnDialog.currentState = "form";
|
|
}
|
|
}
|
|
|
|
ParallelAnimation {
|
|
Anim {
|
|
target: formContent
|
|
property: "opacity"
|
|
to: 1
|
|
duration: Tokens.anim.durations.small
|
|
easing: Tokens.anim.emphasized
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|