// Wi-Fi, at page size. // // The quick settings version is a popover: a compact list you glance at. This // is the one you sit in front of when a network is not behaving, so each row // carries what you would otherwise open a terminal to find out — signal, // security, and whether it is a network this machine already knows. // // Joining a secured network reveals an inline password field rather than // failing silently, which is the one interaction the popover already got right // and is worth keeping identical. import QtQuick import Quickshell import Quickshell.Networking import qs.config import qs.services import qs.widgets Column { id: root spacing: 0 // SSID whose password field is open, and the last failure. property string passwordFor: "" property string failedSsid: "" property string failedText: "" function activate(network: var): void { root.failedSsid = ""; if (network.connected) return; if (network.known || !Connectivity.isSecured(network)) { root.passwordFor = ""; network.connect(); return; } root.passwordFor = root.passwordFor === network.name ? "" : network.name; } Repeater { model: Connectivity.networks Column { id: entry required property var modelData required property int index width: parent.width SettingRow { width: parent.width label: entry.modelData.name || "Hidden network" detail: { const bits = []; if (entry.modelData.connected) bits.push("Connected"); else if (entry.modelData.known) bits.push("Saved"); bits.push(Connectivity.signalLabel(entry.modelData.signalStrength)); bits.push(Connectivity.securityLabel(entry.modelData)); return bits.join(" · "); } divider: entry.index < Connectivity.networks.length - 1 || root.passwordFor === entry.modelData.name controlWidth: 190 activatable: !entry.modelData.connected onActivated: root.activate(entry.modelData) Row { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: 7 Text { anchors.verticalCenter: parent.verticalCenter visible: entry.modelData.connected text: "Connected" color: Theme.accent font.family: Theme.fontFamily font.pixelSize: Theme.fontSize font.weight: Font.DemiBold } SettingsButton { anchors.verticalCenter: parent.verticalCenter visible: entry.modelData.connected text: "Disconnect" onClicked: entry.modelData.disconnect() } SettingsButton { anchors.verticalCenter: parent.verticalCenter visible: !entry.modelData.connected text: entry.modelData.known ? "Connect" : "Join" onClicked: root.activate(entry.modelData) } } } // The password field for this network, when it is the one being // joined. Inline rather than a dialog: a dialog over a tiled window // is a worse place to type than the row you just clicked. Item { width: parent.width height: root.passwordFor === entry.modelData.name ? 54 : 0 visible: height > 0 clip: true onVisibleChanged: { if (visible) password.grab(); else password.clear(); } PasswordField { id: password anchors.left: parent.left anchors.right: joinButton.left anchors.rightMargin: 8 anchors.verticalCenter: parent.verticalCenter placeholder: "Password for " + (entry.modelData.name || "network") onAccepted: joinButton.join() } SettingsButton { id: joinButton anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter text: "Join" function join(): void { entry.modelData.connect(password.text); root.passwordFor = ""; password.text = ""; } onClicked: joinButton.join() } } Text { width: parent.width visible: root.failedSsid === entry.modelData.name leftPadding: 2 bottomPadding: 8 text: root.failedText color: Theme.warn font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } Connections { target: entry.modelData function onConnectionFailed(reason): void { root.failedSsid = entry.modelData.name; root.failedText = Connectivity.connectionFailureText(reason); root.passwordFor = entry.modelData.name; } } } } SettingRow { width: parent.width visible: Connectivity.networks.length === 0 label: !Connectivity.wifiDevice ? "No Wi-Fi adapter" : (Connectivity.wifiEnabled ? "Looking for networks…" : "Wi-Fi is off") detail: Connectivity.wifiDevice && !Connectivity.wifiEnabled ? "Turn it on above to see what is nearby" : "" divider: false } }