// Connections — wired, Wi-Fi, and Bluetooth. // // Wi-Fi and Bluetooth are handled here rather than delegated. Everything goes // through Quickshell.Networking and Quickshell.Bluetooth -- NetworkManager and // BlueZ over DBus -- and nothing shells out to nmcli or bluetoothctl. That was // the founding requirement for this desktop: never having to drop to a terminal // to join a network. // // Scanning follows this page being on screen. Wi-Fi scanning and especially // Bluetooth discovery hold the radio, and doing either for a list nobody is // looking at is battery and airtime spent on nothing. import QtQuick import Quickshell import Quickshell.Networking import qs.config import qs.services SettingsPage { id: root title: "Connections" lede: Connectivity.activeNetwork ? "Connected to " + Connectivity.activeNetwork.name : "Wi-Fi, Bluetooth, and the things Fedora owns." // Drive the scanners only while this page is the one being shown. Component.onCompleted: { Connectivity.active = true; if (!WifiShare.scanned) WifiShare.refresh(); } Component.onDestruction: Connectivity.active = false SettingsCard { title: "Wired" visible: Connectivity.wiredDevice !== null SwitchRow { label: "Ethernet" // Three states worth telling apart: on, off but plugged in, and // nothing in the socket. "Not connected" covered all three and // explained none of them. detail: { const device = Connectivity.wiredDevice; if (!device) return ""; if (device.connected) return device.name + (device.linkSpeed > 0 ? " · " + device.linkSpeed + " Mb/s" : ""); // Not "no cable": that cannot be told apart from "switched off" // by anything reliable here, and guessing produced a switch // that blamed the hardware for what it had just done itself. return device.name + " · off"; } checked: Connectivity.wiredOn enabled: Connectivity.wiredAvailable divider: false onToggled: value => Connectivity.setWired(value) } } SettingsCard { title: "Wi-Fi" // A Wi-Fi switch reading "On" above the words "No Wi-Fi adapter" is a // contradiction; with no radio the card simply does not belong. visible: Connectivity.wifiDevice !== null subtitle: "Networks are re-scanned while this page is open." SettingRow { label: "Wi-Fi" detail: Connectivity.wifiEnabled ? "On" : "Off" controlWidth: 48 divider: Connectivity.wifiEnabled SettingsToggle { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter checked: Connectivity.wifiEnabled enabled: Connectivity.wifiAvailable onToggled: value => Networking.wifiEnabled = value } } WifiPanel { width: parent.width visible: Connectivity.wifiEnabled } } // Sharing a network by QR, the way GNOME's Wi-Fi panel does. The // alternative is reading a passphrase out loud. // // The image holds the password in machine-readable form, so it is generated // on demand rather than up front, and the helper writes it to tmpfs under // XDG_RUNTIME_DIR instead of anywhere persistent. SettingsCard { visible: Connectivity.wifiDevice !== null && WifiShare.shareable.length > 0 title: "Share a network" subtitle: WifiShare.sharing !== "" ? "Point a phone's camera at the code to join " + WifiShare.sharing + "." : "Shows a QR code a phone can scan to join, without reading the password out." Repeater { model: WifiShare.shareable ActionRow { id: shareRow required property var modelData required property int index label: shareRow.modelData.ssid detail: WifiShare.sharing === shareRow.modelData.name ? "Showing a code below — anyone who can see the screen can join" : "Saved network" action: WifiShare.sharing === shareRow.modelData.name ? "Hide" : "Show code" divider: shareRow.index < WifiShare.shareable.length - 1 || WifiShare.sharing !== "" onTriggered: WifiShare.sharing === shareRow.modelData.name ? WifiShare.stopSharing() : WifiShare.share(shareRow.modelData.name) } } // Drawn at its natural size on a white plate: a QR code inverted or // tinted to match a dark theme is unreliable to scan, and this one has // exactly one job. Item { width: parent.width visible: WifiShare.sharing !== "" && WifiShare.imagePath !== "" implicitHeight: visible ? plate.height + 20 : 0 Rectangle { id: plate anchors.horizontalCenter: parent.horizontalCenter y: 10 width: 208 height: 208 radius: 10 color: "white" Image { anchors.centerIn: parent width: 184 height: 184 smooth: false fillMode: Image.PreserveAspectFit cache: false source: WifiShare.imagePath !== "" ? "file://" + WifiShare.imagePath : "" } } } } SettingsCard { title: "Bluetooth" visible: Connectivity.adapter !== null subtitle: "Discovery runs while this page is open." SettingRow { label: "Bluetooth" detail: Connectivity.adapter ? (Connectivity.adapter.enabled ? "On" : "Off") : "Unavailable" controlWidth: 48 divider: !!(Connectivity.adapter && Connectivity.adapter.enabled) SettingsToggle { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter checked: !!(Connectivity.adapter && Connectivity.adapter.enabled) enabled: Connectivity.adapter !== null onToggled: value => { if (Connectivity.adapter) Connectivity.adapter.enabled = value; } } } BluetoothPanel { width: parent.width visible: !!(Connectivity.adapter && Connectivity.adapter.enabled) } } SettingsCard { title: "Owned by Fedora" // These two panels drive NetworkManager over D-Bus, which is why they // work in this session when most GNOME panels do not. They are split // because GNOME splits them: "network" is wired, VPN and proxies, and // does not contain Wi-Fi -- the one panel used to point everyone // there, so the road to a hidden SSID or eduroam ended on a page // without Wi-Fi on it. subtitle: "Wired, VPN and Wi-Fi connection editing stay with GNOME's panels, which drive the same NetworkManager this page reads. Printers and online accounts have their own pages here." ActionRow { label: "Wi-Fi networks" detail: "Hidden networks, enterprise (802.1X) logins, and per-network settings" action: "Open" onTriggered: SystemSettings.openGnomePanel("wifi") } ActionRow { label: "Network connections" detail: "VPN, proxies, and wired connection settings" action: "Open" divider: false onTriggered: SystemSettings.openGnomePanel("network") } } }