diff --git a/quickshell/Modules/Settings/NetworkTab.qml b/quickshell/Modules/Settings/NetworkTab.qml index b9b25661..ace8adc2 100644 --- a/quickshell/Modules/Settings/NetworkTab.qml +++ b/quickshell/Modules/Settings/NetworkTab.qml @@ -1697,8 +1697,11 @@ Item { required property int index readonly property bool isActive: DMSNetworkService.isActiveUuid(modelData.uuid) + readonly property bool isTransient: !!modelData.transient + readonly property bool canExpand: modelData.canExpand !== false + readonly property bool canDelete: modelData.canDelete !== false readonly property bool isExpanded: networkTab.expandedVpnUuid === modelData.uuid - readonly property var configData: isExpanded ? VPNService.editConfig : null + readonly property var configData: (!isTransient && isExpanded) ? VPNService.editConfig : null width: parent.width height: isExpanded ? 56 + vpnExpandedContent.height : 56 @@ -1745,7 +1748,7 @@ Item { Column { spacing: 2 anchors.verticalCenter: parent.verticalCenter - width: parent.width - 20 - 28 - 28 - Theme.spacingS * 4 + width: parent.width - 20 - ((canExpand ? 28 : 0) + (canDelete ? 28 : 0)) - Theme.spacingS * 4 StyledText { text: modelData.name @@ -1775,6 +1778,7 @@ Item { radius: 14 color: vpnExpandBtn.containsMouse ? Theme.surfacePressed : "transparent" anchors.verticalCenter: parent.verticalCenter + visible: canExpand DankIcon { anchors.centerIn: parent @@ -1805,6 +1809,7 @@ Item { radius: 14 color: vpnDeleteBtn.containsMouse ? Theme.errorHover : "transparent" anchors.verticalCenter: parent.verticalCenter + visible: canDelete DankIcon { anchors.centerIn: parent @@ -1835,7 +1840,7 @@ Item { id: vpnExpandedContent width: parent.width spacing: Theme.spacingXS - visible: isExpanded + visible: !isTransient && isExpanded Rectangle { width: parent.width diff --git a/quickshell/Services/DMSNetworkService.qml b/quickshell/Services/DMSNetworkService.qml index 471480db..6795bb6a 100644 --- a/quickshell/Services/DMSNetworkService.qml +++ b/quickshell/Services/DMSNetworkService.qml @@ -81,7 +81,46 @@ Singleton { property string pendingVpnUuid: "" property var vpnBusyStartTime: 0 - property alias profiles: root.vpnProfiles + property var profiles: { + const mergedProfiles = vpnProfiles ? vpnProfiles.slice() : []; + const seen = new Set(); + + for (const profile of mergedProfiles) { + if (profile?.uuid) + seen.add("uuid:" + profile.uuid); + if (profile?.name) + seen.add("name:" + profile.name); + } + + for (const active of vpnActive || []) { + const entryUuid = active?.uuid || active?.name || ""; + const uuidKey = active?.uuid ? "uuid:" + active.uuid : ""; + const nameKey = active?.name ? "name:" + active.name : ""; + + if ((uuidKey && seen.has(uuidKey)) || (!uuidKey && nameKey && seen.has(nameKey))) + continue; + + mergedProfiles.unshift({ + uuid: entryUuid, + name: active?.name || I18n.tr("Active VPN"), + serviceType: active?.serviceType || "", + type: active?.type || "", + typeLabel: active?.typeLabel || active?.vpnType || "", + state: active?.state || "", + device: active?.device || "", + transient: true, + canDelete: false, + canExpand: false + }); + + if (uuidKey) + seen.add(uuidKey); + if (nameKey) + seen.add(nameKey); + } + + return mergedProfiles; + } property alias activeConnections: root.vpnActive property var activeUuids: vpnActive.map(v => v.uuid).filter(u => !!u) property var activeNames: vpnActive.map(v => v.name).filter(n => !!n) diff --git a/quickshell/Services/VPNService.qml b/quickshell/Services/VPNService.qml index b8483d50..a752c47f 100644 --- a/quickshell/Services/VPNService.qml +++ b/quickshell/Services/VPNService.qml @@ -216,6 +216,8 @@ Singleton { function getVpnTypeFromProfile(profile) { if (!profile) return "VPN"; + if (profile.typeLabel) + return profile.typeLabel; if (profile.type === "wireguard") return "WireGuard"; return getPluginName(profile.serviceType); diff --git a/quickshell/Widgets/VpnProfileDelegate.qml b/quickshell/Widgets/VpnProfileDelegate.qml index e8594565..9a74d97c 100644 --- a/quickshell/Widgets/VpnProfileDelegate.qml +++ b/quickshell/Widgets/VpnProfileDelegate.qml @@ -11,13 +11,16 @@ Rectangle { required property var profile property bool isExpanded: false + readonly property bool isTransient: !!profile?.transient + readonly property bool canExpand: profile?.canExpand !== false + readonly property bool canDelete: profile?.canDelete !== false signal toggleExpand signal deleteRequested readonly property bool isActive: DMSNetworkService.activeUuids?.includes(profile?.uuid) ?? false readonly property bool isHovered: rowArea.containsMouse || expandBtn.containsMouse || deleteBtn.containsMouse - readonly property var configData: isExpanded ? VPNService.editConfig : null + readonly property var configData: (!isTransient && isExpanded) ? VPNService.editConfig : null readonly property var configFields: buildConfigFields() height: isExpanded ? 46 + expandedContent.height : 46 @@ -114,7 +117,7 @@ Rectangle { Column { spacing: 1 anchors.verticalCenter: parent.verticalCenter - width: parent.width - 20 - 28 - 28 - Theme.spacingS * 4 + width: parent.width - 20 - ((canExpand ? 28 : 0) + (canDelete ? 28 : 0)) - Theme.spacingS * 4 StyledText { text: profile?.name ?? "" @@ -148,6 +151,7 @@ Rectangle { radius: 14 color: expandBtn.containsMouse ? Theme.surfacePressed : "transparent" anchors.verticalCenter: parent.verticalCenter + visible: canExpand DankIcon { anchors.centerIn: parent @@ -171,6 +175,7 @@ Rectangle { radius: 14 color: deleteBtn.containsMouse ? Theme.errorHover : "transparent" anchors.verticalCenter: parent.verticalCenter + visible: canDelete DankIcon { anchors.centerIn: parent @@ -227,7 +232,7 @@ Rectangle { Flow { width: parent.width spacing: Theme.spacingXS - visible: !VPNService.configLoading && configData + visible: !isTransient && !VPNService.configLoading && configData Repeater { model: configFields