1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-11 07:52:50 -05:00

VPN multi-active: toggle per row fixes reconnection, add Quick Connect + Disconnect All, header/tooltips summarize multiple active; default allow multiple active

This commit is contained in:
Jon Rogers
2025-08-30 16:28:42 -04:00
parent 7f467b0a0d
commit d1890c69c9
3 changed files with 77 additions and 41 deletions

View File

@@ -77,7 +77,12 @@ Rectangle {
Text { Text {
id: tooltipText id: tooltipText
anchors.centerIn: parent anchors.centerIn: parent
text: VpnService.connected ? ("VPN Connected • " + (VpnService.activeName || "")) : "VPN Disconnected" text: {
if (!VpnService.connected) return "VPN Disconnected"
const names = VpnService.activeNames || []
if (names.length <= 1) return "VPN Connected • " + (names[0] || "")
return "VPN Connected • " + names[0] + " +" + (names.length - 1)
}
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
} }

View File

@@ -162,32 +162,59 @@ DankPopout {
Item { Layout.fillWidth: true; height: 1 } Item { Layout.fillWidth: true; height: 1 }
// Quick connect when not connected // Quick connect
// Rectangle { Rectangle {
// height: 28 height: 28
// radius: 14 radius: 14
// color: quickBtnArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight color: quickBtnArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
// visible: !VpnService.connected && VpnService.profiles.length > 0 visible: VpnService.profiles.length > 0
// width: 120 width: 120
// Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
// border.width: 1 border.width: 1
// border.color: Theme.outlineLight border.color: Theme.outlineLight
//
// Row { Row {
// anchors.centerIn: parent anchors.centerIn: parent
// spacing: Theme.spacingXS spacing: Theme.spacingXS
// DankIcon { name: "link"; size: Theme.fontSizeSmall; color: Theme.surfaceText } DankIcon { name: "link"; size: Theme.fontSizeSmall; color: Theme.surfaceText }
// StyledText { text: "Connect"; font.pixelSize: Theme.fontSizeSmall; color: Theme.surfaceText; font.weight: Font.Medium } StyledText { text: "Connect"; font.pixelSize: Theme.fontSizeSmall; color: Theme.surfaceText; font.weight: Font.Medium }
// } }
//
// MouseArea { MouseArea {
// id: quickBtnArea id: quickBtnArea
// anchors.fill: parent anchors.fill: parent
// hoverEnabled: true hoverEnabled: true
// cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
// onClicked: VpnService.toggle() onClicked: VpnService.toggle()
// } }
// } }
// Disconnect all (visible when any active)
Rectangle {
height: 28
radius: 14
color: discAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
visible: VpnService.connected
width: 130
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
border.width: 1
border.color: Theme.outlineLight
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon { name: "link_off"; size: Theme.fontSizeSmall; color: Theme.surfaceText }
StyledText { text: "Disconnect All"; font.pixelSize: Theme.fontSizeSmall; color: Theme.surfaceText; font.weight: Font.Medium }
}
MouseArea {
id: discAllArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: VpnService.disconnectAllActive()
}
}
} }
Rectangle { height: 1; width: parent.width; color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) } Rectangle { height: 1; width: parent.width; color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) }
@@ -255,13 +282,7 @@ DankPopout {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: VpnService.toggle(modelData.uuid)
if (modelData.uuid === VpnService.activeUuid) {
VpnService.disconnect(modelData.uuid)
} else {
VpnService.connect(modelData.uuid)
}
}
} }
} }
} }

View File

@@ -18,8 +18,9 @@ Singleton {
// [{ name, uuid, type }] // [{ name, uuid, type }]
property var profiles: [] property var profiles: []
// Enforce single active VPN at a time // Allow multiple active VPNs (set true to allow concurrent connections)
property bool singleActive: true // Default: allow multiple, to align with NetworkManager capability
property bool singleActive: false
// Active VPN connections (may be multiple) // Active VPN connections (may be multiple)
// Full list and convenience projections // Full list and convenience projections
@@ -160,11 +161,12 @@ Singleton {
} }
function toggle(uuid) { function toggle(uuid) {
if (root.activeUuid && (uuid === undefined || uuid === root.activeUuid)) { if (uuid) {
disconnect(root.activeUuid) if (isActiveUuid(uuid)) disconnect(uuid)
} else if (uuid) { else connect(uuid)
connect(uuid) return
} else if (root.profiles.length > 0) { }
if (root.profiles.length > 0) {
connect(root.profiles[0].uuid) connect(root.profiles[0].uuid)
} }
} }
@@ -209,6 +211,14 @@ Singleton {
} }
} }
function disconnectAllActive() {
if (root.isBusy) return
root.isBusy = true
const script = `nmcli -t -f UUID,TYPE connection show --active | awk -F: '$2 ~ /^(vpn|wireguard)$/ {print $1}' | while read u; do [ -n \"$u\" ] && nmcli connection down uuid \"$u\" || true; done`
vpnSwitch.command = ["bash", "-lc", script]
vpnSwitch.running = true
}
// Sequenced down/up using a single shell for exclusive switch // Sequenced down/up using a single shell for exclusive switch
Process { Process {
id: vpnSwitch id: vpnSwitch