diff --git a/config/dot/quickshell/modules/quicksettings/QuickSettingsPanel.qml b/config/dot/quickshell/modules/quicksettings/QuickSettingsPanel.qml index 76eead9..60346dd 100644 --- a/config/dot/quickshell/modules/quicksettings/QuickSettingsPanel.qml +++ b/config/dot/quickshell/modules/quicksettings/QuickSettingsPanel.qml @@ -107,6 +107,27 @@ Item { onExpanded: root.expand("wifi") } + // Only present when there is a wired device at all -- most machines + // this runs on have one, but a laptop without a dock does not, and + // an Ethernet tile there would be a control for absent hardware. + Toggle { + width: root.cellWidth + visible: Connectivity.wiredDevice !== null + icon: Connectivity.wiredOn + ? "network-wired-symbolic" + : "network-wired-disconnected-symbolic" + label: "Ethernet" + active: Connectivity.wiredOn + enabled: Connectivity.wiredAvailable + sublabel: { + if (Connectivity.wiredOn) + return Connectivity.wiredDevice.linkSpeed > 0 + ? Connectivity.wiredDevice.linkSpeed + " Mb/s" : "Connected"; + return "Off"; + } + onToggled: Connectivity.setWired(!Connectivity.wiredOn) + } + Toggle { width: root.cellWidth icon: root.btAdapter && root.btAdapter.enabled ? "bluetooth-active-symbolic" : "bluetooth-disabled-symbolic" diff --git a/config/dot/quickshell/modules/settings/ConnectivityPage.qml b/config/dot/quickshell/modules/settings/ConnectivityPage.qml index a8f379a..475224a 100644 --- a/config/dot/quickshell/modules/settings/ConnectivityPage.qml +++ b/config/dot/quickshell/modules/settings/ConnectivityPage.qml @@ -37,11 +37,27 @@ SettingsPage { title: "Wired" visible: Connectivity.wiredDevice !== null - TextRow { + SwitchRow { label: "Ethernet" - detail: Connectivity.wiredDevice ? Connectivity.wiredDevice.name : "" - value: Connectivity.wiredDevice && Connectivity.wiredDevice.connected ? "Connected" : "Not connected" + // 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) } } diff --git a/config/dot/quickshell/services/Connectivity.qml b/config/dot/quickshell/services/Connectivity.qml index ed22139..ad86105 100644 --- a/config/dot/quickshell/services/Connectivity.qml +++ b/config/dot/quickshell/services/Connectivity.qml @@ -45,6 +45,40 @@ Singleton { return fallback; } + // Whether the wired connection is on, as a person means it: carrying + // traffic, and set to come back by itself. + readonly property bool wiredOn: root.wiredDevice !== null + && root.wiredDevice.connected + + // Deliberately NOT gated on hasLink. That property reads false while the + // device is merely disconnected -- NetworkManager still reports the carrier + // as on -- so using it to decide whether the switch works built a trap + // door: turning Ethernet off made the switch disable itself, claim "no + // cable", and leave no way to turn it back on. A wired device that exists + // can always be asked to come up; if there is genuinely no cable, the + // attempt fails and says so, which is the honest failure. + readonly property bool wiredAvailable: root.wiredDevice !== null + + // Turning wired networking off means both halves. Disconnecting alone lasts + // about a second: NetworkManager sees a managed device with autoconnect set + // and immediately brings it back, so the switch would flip itself on again + // and read as broken. + function setWired(enabled: bool): void { + const device = root.wiredDevice; + if (!device) + return; + device.autoconnect = enabled; + if (enabled) { + // With autoconnect restored NetworkManager will usually bring it up + // on its own; asking directly makes it immediate rather than + // whenever the daemon next looks. + if (device.network) + device.network.connect(); + } else { + device.disconnect(); + } + } + readonly property var adapter: Bluetooth.defaultAdapter readonly property bool wifiEnabled: Networking.wifiEnabled