// VPN profile picker: every saved VPN or WireGuard connection, active first. // Clicking a row flips that one profile, so a machine with several tunnels // can switch without a trip through nmcli — which is the whole reason this // list exists (see services/Vpn.qml). import QtQuick import qs.config import qs.services Item { id: root implicitHeight: list.implicitHeight readonly property var profiles: { const list = Vpn.connections.slice(); list.sort((a, b) => { if (a.active !== b.active) return a.active ? -1 : 1; return (a.name || "").localeCompare(b.name || ""); }); return list; } function stateText(profile): string { const kind = profile.kind === "wireguard" ? "WireGuard" : "VPN"; return profile.active ? kind + " · Connected" : kind; } ScrollColumn { id: list anchors.fill: parent maxHeight: 300 Text { width: parent.width visible: root.profiles.length === 0 topPadding: 12 bottomPadding: 12 horizontalAlignment: Text.AlignHCenter text: "No VPN profiles" color: Theme.fgMuted font.family: Theme.fontFamily font.pixelSize: Theme.fontSize } Repeater { model: root.profiles RowButton { required property var modelData width: parent.width icon: "network-vpn-symbolic" iconFallback: "network-workgroup-symbolic" label: modelData.name sublabel: root.stateText(modelData) selected: modelData.active dimmed: Vpn.busy onClicked: Vpn.setActive(modelData.uuid, !modelData.active) } } // Activation failures land here rather than vanishing: "the toggle // did nothing" was exactly the complaint that motivated this panel. Text { width: parent.width visible: Vpn.lastError !== "" topPadding: 4 bottomPadding: 8 leftPadding: 12 rightPadding: 12 wrapMode: Text.Wrap text: Vpn.lastError color: Theme.warn font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } } }