quickshell: add wifi network selector dropdown
Click network icon to see available wifi networks with signal strength. Click to connect, disconnect button for active network. Uses BarDropdown component for consistent styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
074e298f32
commit
4a0399c6d3
1 changed files with 189 additions and 3 deletions
|
|
@ -595,14 +595,56 @@ in
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var wifiNetworks: []
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: nmEditorProc
|
id: wifiScanProc
|
||||||
command: ["${pkgs.networkmanagerapplet}/bin/nm-connection-editor"]
|
command: ["${pkgs.networkmanager}/bin/nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY,IN-USE", "device", "wifi", "list", "--rescan", "auto"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: data => {
|
||||||
|
let fields = data.split(":");
|
||||||
|
if (fields.length < 4 || fields[0] === "") return;
|
||||||
|
let nets = netWidget.wifiNetworks;
|
||||||
|
// Avoid duplicates
|
||||||
|
for (let i = 0; i < nets.length; i++) {
|
||||||
|
if (nets[i].ssid === fields[0]) return;
|
||||||
|
}
|
||||||
|
nets.push({
|
||||||
|
ssid: fields[0],
|
||||||
|
signal: parseInt(fields[1]) || 0,
|
||||||
|
security: fields[2],
|
||||||
|
active: fields[3] === "*"
|
||||||
|
});
|
||||||
|
netWidget.wifiNetworks = nets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: wifiConnectProc
|
||||||
|
property string targetSsid: ""
|
||||||
|
command: ["${pkgs.networkmanager}/bin/nmcli", "device", "wifi", "connect", targetSsid]
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: netDisconnectProc
|
||||||
|
command: ["${pkgs.networkmanager}/bin/nmcli", "device", "disconnect", "wlan0"]
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: nmEditorProc.running = true
|
onClicked: {
|
||||||
|
if (netDropdown.justDismissed) return;
|
||||||
|
if (netDropdown.visible) {
|
||||||
|
netDropdown.visible = false;
|
||||||
|
} else {
|
||||||
|
netWidget.wifiNetworks = [];
|
||||||
|
wifiScanProc.running = true;
|
||||||
|
let pos = netWidget.mapToItem(bar.contentItem, netWidget.width / 2, 0);
|
||||||
|
netDropdown.dropdownX = pos.x;
|
||||||
|
netDropdown.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -917,6 +959,150 @@ in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Network dropdown
|
||||||
|
BarDropdown {
|
||||||
|
id: netDropdown
|
||||||
|
fullWidth: netDropdownCol.width + 24
|
||||||
|
fullHeight: netDropdownCol.height + 16
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: netDropdownCol
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
width: 220
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
// Current connection header
|
||||||
|
Text {
|
||||||
|
width: parent.width
|
||||||
|
text: netWidget.netState === "connected"
|
||||||
|
? "\u{f05a9} " + netWidget.netConn
|
||||||
|
: "\u{f05aa} Not connected"
|
||||||
|
color: "#${c.base05}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.weight: Font.Medium
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect button (when connected)
|
||||||
|
Rectangle {
|
||||||
|
visible: netWidget.netState === "connected"
|
||||||
|
width: parent.width
|
||||||
|
height: 28
|
||||||
|
color: disconnectMouse.containsMouse ? "#${c.base02}" : "transparent"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Disconnect"
|
||||||
|
color: "#${c.base08}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: disconnectMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
netDisconnectProc.running = true;
|
||||||
|
netDropdown.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width - 20
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 1
|
||||||
|
color: "#${c.base03}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available networks header
|
||||||
|
Text {
|
||||||
|
text: "Available networks"
|
||||||
|
color: "#${c.base03}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 11
|
||||||
|
topPadding: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network list
|
||||||
|
Repeater {
|
||||||
|
model: netWidget.wifiNetworks
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
width: 220
|
||||||
|
height: 32
|
||||||
|
color: netItemMouse.containsMouse ? "#${c.base02}" : "transparent"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Signal icon
|
||||||
|
Text {
|
||||||
|
text: {
|
||||||
|
let s = modelData.signal;
|
||||||
|
if (s >= 75) return "\u{f05a9}";
|
||||||
|
if (s >= 50) return "\u{f05a9}";
|
||||||
|
if (s >= 25) return "\u{f05a9}";
|
||||||
|
return "\u{f05aa}";
|
||||||
|
}
|
||||||
|
color: modelData.active ? "#${c.base0B}" : "#${c.base04}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 13
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSID
|
||||||
|
Text {
|
||||||
|
text: modelData.ssid
|
||||||
|
color: modelData.active ? "#${c.base0B}" : "#${c.base05}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 12
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: 140
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock icon for secured networks
|
||||||
|
Text {
|
||||||
|
visible: modelData.security !== "" && modelData.security !== "--"
|
||||||
|
text: "\u{f0341}"
|
||||||
|
color: "#${c.base03}"
|
||||||
|
font.family: "FiraMono Nerd Font"
|
||||||
|
font.pixelSize: 10
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: netItemMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.active) {
|
||||||
|
wifiConnectProc.targetSsid = modelData.ssid;
|
||||||
|
wifiConnectProc.running = true;
|
||||||
|
}
|
||||||
|
netDropdown.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calendar popup
|
// Calendar popup
|
||||||
BarDropdown {
|
BarDropdown {
|
||||||
id: calPopup
|
id: calPopup
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue