// GNOME's system menu button: a small run of status glyphs — network, volume, // bluetooth, caffeine — that opens the quick settings panel when clicked. // // This widget only *reports* state. The quick settings panel itself lives // elsewhere; clicking here just raises `requestQuickSettings`. import Quickshell import Quickshell.Bluetooth import Quickshell.Networking import Quickshell.Services.Pipewire import QtQuick import qs.config import qs.services import qs.widgets Pill { id: root signal requestQuickSettings // Set by the shell when an idle inhibitor is held. Lives outside this // widget because the inhibitor itself is owned by the quick settings side. property bool idleInhibited: false onActivated: root.requestQuickSettings() // Right-click opens the settings that govern this widget. Network and Bluetooth, which is most of what these glyphs report. onSecondaryActivated: ShellState.openSettings("connectivity") // ── Audio ─────────────────────────────────────────────────────────────── // Without a tracker, volume and muted silently read as zero/false. PwObjectTracker { objects: [Pipewire.defaultAudioSink] } // defaultAudioSink is transiently null while pipewire settles. readonly property real volume: Pipewire.defaultAudioSink?.audio?.volume ?? 0 readonly property bool muted: Pipewire.defaultAudioSink?.audio?.muted ?? false // ── Network ───────────────────────────────────────────────────────────── readonly property var devices: Networking.devices ? Networking.devices.values : [] readonly property var wiredDevice: root.devices.find(d => d.type === DeviceType.Wired && d.connected) ?? null readonly property var wifiDevice: root.devices.find(d => d.type === DeviceType.Wifi) ?? null readonly property var wifiNetwork: { const networks = root.wifiDevice?.networks ? root.wifiDevice.networks.values : []; return networks.find(n => n.connected) ?? null; } // ── Bluetooth ─────────────────────────────────────────────────────────── readonly property bool bluetoothOn: Bluetooth.defaultAdapter?.enabled ?? false readonly property bool bluetoothConnected: { const devices = Bluetooth.devices ? Bluetooth.devices.values : []; return devices.some(d => d.connected); } StatusGlyph { glyph: { if (root.wiredDevice) return "\u{F0200}"; // md-ethernet if (!root.wifiNetwork) return "\u{F092D}"; // md-wifi_strength_off // NetworkManager reports strength 0-100; some backends normalize to // 0-1, so accept either rather than showing one bar forever. const raw = root.wifiNetwork.signalStrength ?? 0; const strength = raw <= 1 ? raw * 100 : raw; if (strength >= 75) return "\u{F0928}"; // md-wifi_strength_4 if (strength >= 50) return "\u{F0925}"; // md-wifi_strength_3 if (strength >= 25) return "\u{F0922}"; // md-wifi_strength_2 return "\u{F091F}"; // md-wifi_strength_1 } color: { if (root.wiredDevice) return Theme.barFg; if (!root.wifiNetwork) return Theme.barFgMuted; return Networking.connectivity === NetworkConnectivity.Full ? Theme.barFg : Theme.warn; } } // The active layout, only where there is a choice to indicate: a machine // with one layout knows what its keys say. GNOME shows the same short // code in the same corner, which is the muscle memory this preserves. Text { visible: KeyboardLayout.multiple anchors.verticalCenter: parent.verticalCenter text: KeyboardLayout.shortLabel color: Theme.barFg font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.Medium font.letterSpacing: 0.5 } // A tunnel that is up changes what every connection means, so it earns a // permanent glyph while active -- and its absence is the resting state, // same shape as Bluetooth below. StatusGlyph { visible: Vpn.anyActive glyph: "\u{F0582}" // md-vpn color: Theme.accent } StatusGlyph { glyph: { if (root.muted || root.volume <= 0) return "\u{F075F}"; // md-volume_mute if (root.volume < 0.34) return "\u{F057F}"; // md-volume_low if (root.volume < 0.67) return "\u{F0580}"; // md-volume_medium return "\u{F057E}"; // md-volume_high } color: root.muted ? Theme.barFgMuted : Theme.barFg } StatusGlyph { // Bluetooth off is the resting state on this machine, so the glyph is // simply absent rather than shown crossed out. visible: root.bluetoothOn glyph: root.bluetoothConnected ? "\u{F00B1}" : "\u{F00AF}" // md-bluetooth_connect / md-bluetooth color: root.bluetoothConnected ? Theme.accent : Theme.barFg } StatusGlyph { visible: root.idleInhibited glyph: "\u{F0176}" // md-coffee color: Theme.warn } StatusGlyph { visible: KdeConnect.phoneReachable glyph: "\u{F03F2}" // md-cellphone-link color: Theme.cyan } // Battery. Absent entirely on a desktop: `available` is false until a // battery has actually been read, so this is not a zero that looks like a // flat cell. Same shape as the graphics field in VitalsWidget, which gates // on both the preference and the hardware. // // One color for the icon and the number beside it: two different colors // would read as two indicators. readonly property color batteryColor: { if (Battery.critical) return Theme.danger; if (Battery.low) return Theme.warn; if (Battery.charging) return Theme.ok; return Theme.barFg; } StatusGlyph { visible: Settings.showBattery && Battery.available glyph: { if (Battery.charging) return "\u{F0084}"; // md-battery_charging const level = Math.round(Battery.percent / 10) * 10; if (level >= 100) return "\u{F0079}"; // md-battery if (level <= 0) return "\u{F008E}"; // md-battery_outline // md-battery_10 .. md-battery_90 are consecutive from F007A. return String.fromCodePoint(0xF007A + (level / 10) - 1); } color: root.batteryColor } // The exact number, for the people who ask the icon to be more specific -- // GNOME's "Show Battery Percentage", living under the same gates as the // icon it annotates. Text { visible: Settings.showBattery && Settings.showBatteryPercent && Battery.available anchors.verticalCenter: parent.verticalCenter text: Math.round(Battery.percent) + "%" color: root.batteryColor font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall Behavior on color { ColorAnimation { duration: Theme.durFast } } } }