network chooser: click to show known available connections

This commit is contained in:
Damocles 2026-04-12 18:24:36 +02:00
parent 20d3ad12f9
commit a230b72344
4 changed files with 191 additions and 1 deletions

View file

@ -102,7 +102,7 @@ PanelWindow {
// Connectivity // Connectivity
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D borderColor: M.Theme.base0D
M.Network { visible: M.Modules.network.enable } M.Network { bar: bar; visible: M.Modules.network.enable }
M.Bluetooth {} M.Bluetooth {}
} }

View file

@ -1,4 +1,5 @@
import QtQuick import QtQuick
import Quickshell
import Quickshell.Io import Quickshell.Io
import "." as M import "." as M
@ -91,4 +92,20 @@ M.BarSection {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
required property var bar
TapHandler {
cursorShape: Qt.PointingHandCursor
onTapped: menuLoader.active = !menuLoader.active
}
Loader {
id: menuLoader
active: false
sourceComponent: M.NetworkMenu {
screen: root.bar.screen
anchorX: root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0)
onDismissed: menuLoader.active = false
}
}
} }

172
modules/NetworkMenu.qml Normal file
View file

@ -0,0 +1,172 @@
import QtQuick
import Quickshell
import Quickshell.Io
import "." as M
M.PopupPanel {
id: menuWindow
panelWidth: 250
property var _networks: []
Process {
id: scanner
running: true
command: ["sh", "-c",
"echo '---CONNS---';" +
"nmcli -t -f NAME,UUID,TYPE,ACTIVE connection show 2>/dev/null;" +
"echo '---WIFI---';" +
"nmcli -t -f SSID,SIGNAL device wifi list --rescan no 2>/dev/null"
]
stdout: StdioCollector {
onStreamFinished: {
const sections = text.split("---WIFI---");
const connLines = (sections[0] || "").split("---CONNS---")[1] || "";
const wifiLines = sections[1] || "";
// Visible SSIDs with signal
const visible = {};
for (const l of wifiLines.trim().split("\n")) {
if (!l) continue;
const parts = l.split(":");
const ssid = parts[0];
if (ssid) visible[ssid] = parseInt(parts[1]) || 0;
}
// Saved connections filter: show wired always, wifi only if visible
const nets = [];
for (const l of connLines.trim().split("\n")) {
if (!l) continue;
const parts = l.split(":");
const name = parts[0];
const uuid = parts[1];
const type = parts[2] || "";
const active = parts[3] === "yes";
const isWifi = type.includes("wireless");
if (isWifi && !(name in visible)) continue;
nets.push({
name: name,
uuid: uuid,
isWifi: isWifi,
active: active,
signal: isWifi ? (visible[name] || 0) : -1
});
}
// Active first, then by signal (wifi) or name
nets.sort((a, b) => {
if (a.active !== b.active) return a.active ? -1 : 1;
if (a.signal >= 0 && b.signal >= 0) return b.signal - a.signal;
return a.name.localeCompare(b.name);
});
menuWindow._networks = nets;
}
}
}
Process {
id: connectProc
property string uuid: ""
command: ["nmcli", "connection", "up", uuid]
onRunningChanged: if (!running) scanner.running = true
}
Process {
id: disconnectProc
property string uuid: ""
command: ["nmcli", "connection", "down", uuid]
onRunningChanged: if (!running) scanner.running = true
}
Repeater {
model: menuWindow._networks
delegate: Item {
id: entry
required property var modelData
required property int index
width: menuWindow.panelWidth
height: 32
Rectangle {
anchors.fill: parent
anchors.leftMargin: 4
anchors.rightMargin: 4
color: entryArea.containsMouse ? M.Theme.base02 : "transparent"
radius: M.Theme.radius
}
Text {
id: netIcon
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.isWifi ? "\uF1EB" : "\uDB80\uDE00"
color: entry.modelData.active ? M.Theme.base0D : M.Theme.base05
font.pixelSize: M.Theme.fontSize + 1
font.family: M.Theme.iconFontFamily
}
Text {
anchors.left: netIcon.right
anchors.leftMargin: 8
anchors.right: sigLabel.left
anchors.rightMargin: 4
anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.name
color: entry.modelData.active ? M.Theme.base0D : M.Theme.base05
font.pixelSize: M.Theme.fontSize
font.family: M.Theme.fontFamily
font.bold: entry.modelData.active
elide: Text.ElideRight
}
Text {
id: sigLabel
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.signal >= 0 ? entry.modelData.signal + "%" : ""
color: M.Theme.base04
font.pixelSize: M.Theme.fontSize - 1
font.family: M.Theme.fontFamily
width: entry.modelData.signal >= 0 ? implicitWidth : 0
}
MouseArea {
id: entryArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (entry.modelData.active) {
disconnectProc.uuid = entry.modelData.uuid;
disconnectProc.running = true;
} else {
connectProc.uuid = entry.modelData.uuid;
connectProc.running = true;
}
menuWindow.dismiss();
}
}
}
}
// Empty state
Text {
visible: menuWindow._networks.length === 0
width: menuWindow.panelWidth
height: 32
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "No networks available"
color: M.Theme.base04
font.pixelSize: M.Theme.fontSize
font.family: M.Theme.fontFamily
}
}

View file

@ -22,6 +22,7 @@ Battery 1.0 Battery.qml
Mpris 1.0 Mpris.qml Mpris 1.0 Mpris.qml
MprisMenu 1.0 MprisMenu.qml MprisMenu 1.0 MprisMenu.qml
Network 1.0 Network.qml Network 1.0 Network.qml
NetworkMenu 1.0 NetworkMenu.qml
Bluetooth 1.0 Bluetooth.qml Bluetooth 1.0 Bluetooth.qml
Backlight 1.0 Backlight.qml Backlight 1.0 Backlight.qml
Cpu 1.0 Cpu.qml Cpu 1.0 Cpu.qml