Files
Panama/config/dot/quickshell/modules/settings/ConnectivityPage.qml
T
Gabriel Brown 3d21e20041 Make every control tell the truth
The audit's first tier, in one change: every case found where the interface
asserted something the system did not do.

Twenty-one compositor-owned preferences -- the whole Mouse & Touchpad page,
plus window layout, snapping, dim-inactive and the magnifier -- had the live
half (hyprctl eval) and not the config-time half, so they quietly reverted on
every hyprctl reload. All 70 hypr-backed keys now have a prefs.get() in the
Lua, and hypr-prefs-contract pins both the presence and that the Lua fallback
equals the schema default, which is how the touchpad page misreported natural
scrolling on first boot.

The idle generator fell back from unwritten battery keys to the AC values
while the Power page displayed the schema defaults: a fresh laptop showed
"suspend at 20 minutes" and generated no suspend listener, then discharged to
zero in a bag. Unwritten keys now use the defaults the page shows
(idle-defaults-contract pins generator to schema; idle-config-contract
re-pinned to the new rule with the tradeoff recorded), and change-settings
enables managed idle on any machine with a battery -- without starting
hypridle in whatever session the installer runs under.

The per-app lock-screen notification switches wrote fields nothing read:
hyprlock cannot render notifications. Removed, with the rule model shrunk to
{enabled}, stale stored fields dropped at normalization, and the contract now
forbidding the page from growing lock-screen switches it cannot honor.

The battery warning thresholds were searchable, documented as "Found on
Power & Lock", and rendered nowhere -- and crossing the low threshold changed
only a glyph's color. Both sliders now exist where search was already sending
people, and low battery publishes a real notification at important priority.

Three handoffs opened GNOME panels that are inert in a Hyprland session. The
keyboard handoff is gone (that panel writes gsettings nothing here reads, and
the working controls sat on the same page); Connectivity gains a Wi-Fi row
that opens GNOME's actual Wi-Fi panel -- hidden SSIDs and 802.1X finally have
a road -- beside the network row that legitimately drives NetworkManager; the
universal-access handoff is gone, its few working toggles being controls this
app already owns. And the accessibility page now gives the true reason sticky
keys are missing: each Wayland compositor implements its own and Hyprland
does not yet -- not "an X11 feature with no Wayland equivalent," which sent
people to the wrong conclusion about the platform.

Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
2026-08-23 11:43:39 -04:00

213 lines
7.9 KiB
QML

// Network & Devices.
//
// 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: "Network & Devices"
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")
}
}
}