diff --git a/quickshell/Modules/ControlCenter/ControlCenterContent.qml b/quickshell/Modules/ControlCenter/ControlCenterContent.qml index 730520056..cdf649eb1 100644 --- a/quickshell/Modules/ControlCenter/ControlCenterContent.qml +++ b/quickshell/Modules/ControlCenter/ControlCenterContent.qml @@ -16,6 +16,9 @@ FocusScope { required property var host + property bool live: Window.window?.visible ?? false + property bool audioRefHeld: false + LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true @@ -75,7 +78,23 @@ FocusScope { implicitHeight: targetImplicitHeight focus: true - Component.onCompleted: WidgetUtils.ensureEditButton() + function syncAudioRef(wanted) { + if (wanted === audioRefHeld) + return; + audioRefHeld = wanted; + if (wanted) { + AudioService.addRef(); + return; + } + AudioService.removeRef(); + } + + onLiveChanged: syncAudioRef(live) + Component.onCompleted: { + WidgetUtils.ensureEditButton(); + syncAudioRef(live); + } + Component.onDestruction: syncAudioRef(false) onPlacedWidgetIdsChanged: WidgetUtils.ensureEditButton() function navigateTo(section) { diff --git a/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml b/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml index 5bf2a97b9..dc1fe8147 100644 --- a/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml +++ b/quickshell/Modules/ControlCenter/Widgets/BatteryTile.qml @@ -7,6 +7,8 @@ import qs.Widgets CcTile { id: root + property bool live: Window.window?.visible ?? false + property bool batteryRefHeld: false readonly property bool available: BatteryService.batteryAvailable readonly property bool profileMode: !available && PowerProfileWatcher.available @@ -28,6 +30,21 @@ CcTile { return `${BatteryService.batteryLevel}%`; } dockedText: available || profileMode ? subtitle : title + + function syncBatteryRef(wanted) { + if (wanted === batteryRefHeld) + return; + batteryRefHeld = wanted; + if (wanted) { + BatteryService.addRef(); + return; + } + BatteryService.removeRef(); + } + + onLiveChanged: syncBatteryRef(live) + Component.onCompleted: syncBatteryRef(live) + Component.onDestruction: syncBatteryRef(false) active: available && (BatteryService.isCharging || BatteryService.isPluggedIn) opensPage: !profileMode showExpand: profileMode diff --git a/quickshell/Modules/DBar/Popouts/BatteryPopoutContent.qml b/quickshell/Modules/DBar/Popouts/BatteryPopoutContent.qml index 94bdb0868..edde9577e 100644 --- a/quickshell/Modules/DBar/Popouts/BatteryPopoutContent.qml +++ b/quickshell/Modules/DBar/Popouts/BatteryPopoutContent.qml @@ -13,6 +13,7 @@ Item { id: root property bool active: false + property bool batteryRefHeld: false property real contentPadding: PopoutMetrics.contentPadding readonly property var historyDevice: BatteryService.usePreferred ? BatteryService.preferredDevice : BatteryService.device readonly property bool multipleBatteries: !BatteryService.usePreferred && BatteryService.batteries.length > 1 @@ -72,10 +73,24 @@ Item { LayoutMirroring.enabled: I18n.isRtl LayoutMirroring.childrenInherit: true + function syncBatteryRef(wanted) { + if (wanted === batteryRefHeld) + return; + batteryRefHeld = wanted; + if (wanted) { + BatteryService.addRef(); + return; + } + BatteryService.removeRef(); + } + onActiveChanged: { + syncBatteryRef(active); if (active) focusTimer.restart(); } + Component.onCompleted: syncBatteryRef(active) + Component.onDestruction: syncBatteryRef(false) Keys.onEscapePressed: event => { dismissRequested(); event.accepted = true; diff --git a/quickshell/Modules/DBar/Widgets/Battery.qml b/quickshell/Modules/DBar/Widgets/Battery.qml index 27a1784e7..b141ecff7 100644 --- a/quickshell/Modules/DBar/Widgets/Battery.qml +++ b/quickshell/Modules/DBar/Widgets/Battery.qml @@ -9,6 +9,9 @@ BasePill { id: battery readonly property var log: Log.scoped("Battery") + Component.onCompleted: BatteryService.addRef() + Component.onDestruction: BatteryService.removeRef() + property bool batteryPopupVisible: false property var popoutTarget: null property var widgetData: null diff --git a/quickshell/Modules/DDash/MediaPlayerTab.qml b/quickshell/Modules/DDash/MediaPlayerTab.qml index 4d0eaaf05..2d1ce7ddf 100644 --- a/quickshell/Modules/DDash/MediaPlayerTab.qml +++ b/quickshell/Modules/DDash/MediaPlayerTab.qml @@ -14,6 +14,7 @@ Item { readonly property real stableLength: MprisController.activePlayerStableLength property var allPlayers: MprisController.availablePlayers property bool live: Window.window?.visible ?? false + property bool audioRefHeld: false property string entryId: "media" readonly property var options: DashRegistry.resolvedOptions(entryId) readonly property string playerStyle: options?.playerStyle ?? MediaOptions.defaultPlayerStyle @@ -125,7 +126,22 @@ Item { onTriggered: root.activePlayer?.positionChanged() } + function syncAudioRef(wanted) { + if (wanted === audioRefHeld) + return; + audioRefHeld = wanted; + if (wanted) { + AudioService.addRef(); + return; + } + AudioService.removeRef(); + } + + Component.onCompleted: syncAudioRef(live) + Component.onDestruction: syncAudioRef(false) + onLiveChanged: { + syncAudioRef(live); if (live) { restoreLyrics(); return; diff --git a/quickshell/Modules/DDash/Overview/BatteryCard.qml b/quickshell/Modules/DDash/Overview/BatteryCard.qml index f1d88f8cd..507c93719 100644 --- a/quickshell/Modules/DDash/Overview/BatteryCard.qml +++ b/quickshell/Modules/DDash/Overview/BatteryCard.qml @@ -9,6 +9,7 @@ Card { id: root property bool live: Window.window?.visible ?? false + property bool batteryRefHeld: false readonly property bool wide: width >= DashMetrics.gridRowUnit * 2 readonly property bool tall: height >= DashMetrics.gridRowUnit * 2 @@ -20,6 +21,21 @@ Card { entryId: "battery" tone: options.tone ?? "" + function syncBatteryRef(wanted) { + if (wanted === batteryRefHeld) + return; + batteryRefHeld = wanted; + if (wanted) { + BatteryService.addRef(); + return; + } + BatteryService.removeRef(); + } + + onLiveChanged: syncBatteryRef(live) + Component.onCompleted: syncBatteryRef(live) + Component.onDestruction: syncBatteryRef(false) + Row { anchors.left: parent.left anchors.right: parent.right diff --git a/quickshell/Modules/Settings/AudioTab.qml b/quickshell/Modules/Settings/AudioTab.qml index 737dda87e..cf62dc660 100644 --- a/quickshell/Modules/Settings/AudioTab.qml +++ b/quickshell/Modules/Settings/AudioTab.qml @@ -85,10 +85,12 @@ Item { } Component.onCompleted: { + AudioService.addRef(); hiddenOutputDeviceNames = SessionData.hiddenOutputDeviceNames ?? []; hiddenInputDeviceNames = SessionData.hiddenInputDeviceNames ?? []; updateDeviceList(); } + Component.onDestruction: AudioService.removeRef() Connections { target: AudioService diff --git a/quickshell/Modules/Settings/BatteryTab.qml b/quickshell/Modules/Settings/BatteryTab.qml index 4baa9611d..5c312ebe1 100644 --- a/quickshell/Modules/Settings/BatteryTab.qml +++ b/quickshell/Modules/Settings/BatteryTab.qml @@ -26,6 +26,9 @@ Item { property bool chargeLimitSupported: Qt.platform.os === "linux" + Component.onCompleted: BatteryService.addRef() + Component.onDestruction: BatteryService.removeRef() + Process { id: thresholdProbe running: Qt.platform.os === "linux" diff --git a/quickshell/Services/AudioService.qml b/quickshell/Services/AudioService.qml index dd9b35d7c..7a0b5d83c 100644 --- a/quickshell/Services/AudioService.qml +++ b/quickshell/Services/AudioService.qml @@ -31,18 +31,22 @@ Singleton { readonly property bool pipewireReady: pipewireBackend?.ready ?? false readonly property var pipewireNodes: pipewireBackend?.nodes ?? [] readonly property var allNodes: freebsdAudio ? nativeSinks.concat(nativeSources) : pipewireNodes - readonly property var sink: freebsdAudio ? nativeSink : (pipewireBackend?.defaultSink ?? null) - readonly property var source: freebsdAudio ? nativeSource : (pipewireBackend?.defaultSource ?? null) + readonly property var sink: freebsdAudio ? nativeSink : (pipewireBackend?.defaultSink ?? null) + readonly property var source: freebsdAudio ? nativeSource : (pipewireBackend?.defaultSource ?? null) - readonly property bool freeBsdPollingRequested: - (PopoutService.controlCenterPopout?.shouldBeVisible ?? false) - || (PopoutService.dankDashPopout?.shouldBeVisible ?? false) - || (PopoutService.settingsModal?.visible ?? false) + property int refCount: 0 - function isPipewireVideoSource(node) { - return !freebsdAudio - && (pipewireBackend?.isVideoSource(node) ?? false); - } + function addRef() { + refCount++; + } + + function removeRef() { + refCount = Math.max(0, refCount - 1); + } + + function isPipewireVideoSource(node) { + return !freebsdAudio && (pipewireBackend?.isVideoSource(node) ?? false); + } Loader { id: pipewireBackendLoader @@ -79,11 +83,11 @@ Singleton { property bool isSink: true property bool isStream: false property var properties: ({ - "media.class": isSink ? "Audio/Sink" : "Audio/Source", - "device.api": "oss", - "device.name": pcmName, - "device.description": description - }) + "media.class": isSink ? "Audio/Sink" : "Audio/Source", + "device.api": "oss", + "device.name": pcmName, + "device.description": description + }) property QtObject audio: QtObject { property real volume: 0.0 property bool muted: false @@ -275,7 +279,8 @@ Singleton { function refreshSinkPorts(callback) { if (freebsdAudio) { sinkPorts = ({}); - if (callback) callback(); + if (callback) + callback(); return; } // ensure that parsed labels are in English @@ -297,7 +302,8 @@ Singleton { function setSinkPort(sinkName, portName, callback) { if (freebsdAudio) { - if (callback) callback(false, I18n.tr("Port switching is not available with the FreeBSD native audio backend")); + if (callback) + callback(false, I18n.tr("Port switching is not available with the FreeBSD native audio backend")); return; } Proc.runCommand("audio-set-sink-port", ["env", "LC_ALL=C", "pactl", "set-sink-port", sinkName, portName], (output, exitCode) => { @@ -384,7 +390,8 @@ Singleton { function refreshCards(callback) { if (freebsdAudio) { cards = []; - if (callback) callback(); + if (callback) + callback(); return; } Proc.runCommand("audio-list-cards", ["env", "LC_ALL=C", "pactl", "list", "cards"], (output, exitCode) => { @@ -534,7 +541,6 @@ Singleton { return profile.split("+").filter(part => part.startsWith("output:")).map(part => prefix + part.substring(7)); } - function collectSwitchableOutputPorts(cards, hiddenSinkNames) { const entries = []; for (const card of cards || []) { @@ -576,7 +582,8 @@ Singleton { function activateOutputPort(entry, callback) { if (freebsdAudio) { - if (callback) callback(false, I18n.tr("Output profile switching is not available with the FreeBSD native audio backend")); + if (callback) + callback(false, I18n.tr("Output profile switching is not available with the FreeBSD native audio backend")); return; } if (!entry?.cardName || !entry.profile) @@ -966,7 +973,6 @@ EOFCONFIG }, 0); } - function selectSoundTheme(themeName) { if (!themeName) { SettingsData.set("useSystemSoundTheme", false); @@ -988,7 +994,6 @@ EOFCONFIG }, 0); } - function refreshSoundThemes() { if (!soundThemeSupported) return; @@ -1207,7 +1212,6 @@ EOFCONFIG return audio.muted || audio.volume === 0; } - function volumeIcon(volume, muted) { if (muted) return "volume_off"; @@ -1381,7 +1385,6 @@ EOFCONFIG return ""; } - function detectAudioBackend() { Proc.runCommand("audio-detect-os", ["uname", "-s"], (output, exitCode) => { const osName = exitCode === 0 ? (output || "").trim() : ""; @@ -1538,7 +1541,10 @@ EOFCONFIG const percent = Math.max(0, Math.min(100, Math.round(volume * 100))); const key = node.mixerDevice + "|" + node.mixerControl; const writes = Object.assign({}, pendingFreeBsdVolumeWrites); - writes[key] = { node: node, percent: percent }; + writes[key] = { + node: node, + percent: percent + }; pendingFreeBsdVolumeWrites = writes; lastFreeBsdLocalChangeMs = Date.now(); freeBsdVolumeWriteTimer.restart(); @@ -1550,12 +1556,10 @@ EOFCONFIG for (const key of Object.keys(writes)) { const entry = writes[key]; const node = entry.node; - Proc.runCommand("audio-freebsd-volume-" + node.pcmName + "-" + node.mixerControl, - ["mixer", "-f", node.mixerDevice, node.mixerControl + ".volume=" + entry.percent + "%"], - (output, exitCode) => { - if (exitCode !== 0) - log.error("FreeBSD mixer volume update failed:", output); - }, 0); + Proc.runCommand("audio-freebsd-volume-" + node.pcmName + "-" + node.mixerControl, ["mixer", "-f", node.mixerDevice, node.mixerControl + ".volume=" + entry.percent + "%"], (output, exitCode) => { + if (exitCode !== 0) + log.error("FreeBSD mixer volume update failed:", output); + }, 0); } } @@ -1563,12 +1567,10 @@ EOFCONFIG if (!node) return; lastFreeBsdLocalChangeMs = Date.now(); - Proc.runCommand("audio-freebsd-mute-" + node.pcmName + "-" + node.mixerControl, - ["mixer", "-f", node.mixerDevice, node.mixerControl + ".mute=" + (muted ? "1" : "0")], - (output, exitCode) => { - if (exitCode !== 0) - log.error("FreeBSD mixer mute update failed:", output); - }, 0); + Proc.runCommand("audio-freebsd-mute-" + node.pcmName + "-" + node.mixerControl, ["mixer", "-f", node.mixerDevice, node.mixerControl + ".mute=" + (muted ? "1" : "0")], (output, exitCode) => { + if (exitCode !== 0) + log.error("FreeBSD mixer mute update failed:", output); + }, 0); } function setFreeBsdDefaultDevice(node) { @@ -1768,17 +1770,17 @@ EOFCONFIG return `Microphone volume decreased to ${newVolume}%`; } - onFreeBsdPollingRequestedChanged: { - if (root.freebsdAudio && root.freeBsdPollingRequested) - root.refreshFreeBsdDevices(); - } + onRefCountChanged: { + if (root.refCount === 1) + root.refreshFreeBsdDevices(); + } - Timer { - interval: 5000 - repeat: true - running: root.freebsdAudio && root.freeBsdPollingRequested - onTriggered: root.refreshFreeBsdDevices() - } + Timer { + interval: 5000 + repeat: true + running: root.freebsdAudio && root.refCount > 0 + onTriggered: root.refreshFreeBsdDevices() + } IpcHandler { target: "audio" diff --git a/quickshell/Services/BatteryService.qml b/quickshell/Services/BatteryService.qml index c0ad92de6..1ca0a5aef 100644 --- a/quickshell/Services/BatteryService.qml +++ b/quickshell/Services/BatteryService.qml @@ -12,9 +12,6 @@ Singleton { property bool suppressSound: true property bool previousPluggedState: false - // Consumers such as the battery widget and battery-related popouts hold a - // reference while they need fresh battery information. This keeps the - // service independent from UI singletons and avoids creation-time cycles. property int refCount: 0 function addRef() { @@ -100,9 +97,7 @@ Singleton { const acMatch = text.match(/__DMS_AC__:(\d+)/); root.freebsdBatteryAvailable = blocks.length > 0; - root.freebsdBatteryLevel = blocks.length > 0 - ? Math.max(0, Math.min(100, weight > 0 ? weightedPct / weight : (pctCount > 0 ? pctSum / pctCount : 0))) - : 0; + root.freebsdBatteryLevel = blocks.length > 0 ? Math.max(0, Math.min(100, weight > 0 ? weightedPct / weight : (pctCount > 0 ? pctSum / pctCount : 0))) : 0; root.freebsdIsCharging = charging; root.freebsdPluggedIn = acMatch ? acMatch[1] === "1" : charging; root.freebsdChangeRate = totalRateW; @@ -117,17 +112,14 @@ Singleton { const script = 'units=$(sysctl -n hw.acpi.battery.units 2>/dev/null || echo 0); i=0; while [ "$i" -lt "$units" ]; do echo "__DMS_BATTERY_${i}__"; acpiconf -i "$i" 2>/dev/null; i=$((i+1)); done; printf "__DMS_AC__:"; sysctl -n hw.acpi.acline 2>/dev/null || echo 1'; - Proc.runCommand("battery-freebsd-acpi", - ["nice", "-n", "10", "sh", "-c", script], - (output, exitCode) => { - root.freebsdBatteryProbeComplete = true; + Proc.runCommand("battery-freebsd-acpi", ["nice", "-n", "10", "sh", "-c", script], (output, exitCode) => { + root.freebsdBatteryProbeComplete = true; - if (exitCode === 0) - root.applyFreeBsdBatteryState(output); - else - root.freebsdBatteryAvailable = false; - }, - 0); + if (exitCode === 0) + root.applyFreeBsdBatteryState(output); + else + root.freebsdBatteryAvailable = false; + }, 0); } function updateFreeBsdBatteryFallback() { @@ -154,10 +146,7 @@ Singleton { Timer { interval: 15000 repeat: true - running: root.isBSD - && root.batteries.length === 0 - && root.freebsdBatteryAvailable - && root.refCount > 0 + running: root.isBSD && root.batteries.length === 0 && root.freebsdBatteryAvailable && root.refCount > 0 onTriggered: root.refreshFreeBsdBattery() } @@ -186,9 +175,7 @@ Singleton { if (!batteryAvailable) return; - const profileValue = isPluggedIn - ? SettingsData.acProfileName - : SettingsData.batteryProfileName; + const profileValue = isPluggedIn ? SettingsData.acProfileName : SettingsData.batteryProfileName; if (profileValue === "") return; @@ -266,10 +253,7 @@ Singleton { return Math.min(100, Math.round((energy * 100) / cap)); } - return Math.min(100, Math.round( - chargeBatteries.reduce((sum, b) => sum + _chargePercent(b), 0) - / chargeBatteries.length - )); + return Math.min(100, Math.round(chargeBatteries.reduce((sum, b) => sum + _chargePercent(b), 0) / chargeBatteries.length)); } if (_hasUsableCharge(UPower.displayDevice)) @@ -370,14 +354,10 @@ Singleton { return live > 0 ? live : _lastBatteryLevel; } - readonly property bool isCharging: (isBSD && freebsdBatteryAvailable) - ? freebsdIsCharging - : (_hasKnownChargingState ? _currentIsCharging : _lastIsCharging) + readonly property bool isCharging: (isBSD && freebsdBatteryAvailable) ? freebsdIsCharging : (_hasKnownChargingState ? _currentIsCharging : _lastIsCharging) // Is the system plugged in (Is not running on battery) - readonly property bool isPluggedIn: (isBSD && freebsdBatteryAvailable) - ? freebsdPluggedIn - : !UPower.onBattery + readonly property bool isPluggedIn: (isBSD && freebsdBatteryAvailable) ? freebsdPluggedIn : !UPower.onBattery readonly property bool hasBatteryReading: batteryAvailable && batteryLevel > 0 readonly property bool isLowBattery: hasBatteryReading && batteryLevel <= SettingsData.batteryLowThreshold @@ -431,21 +411,9 @@ Singleton { // urgency: "critical" (red), "warning" (orange/important), or "info" function sendAlert(title, message, urgency, icon, notificationType) { if (notificationType === 1) { - const dbusUrgency = urgency === "critical" - ? "critical" - : (urgency === "warning" ? "normal" : "low"); + const dbusUrgency = urgency === "critical" ? "critical" : (urgency === "warning" ? "normal" : "low"); - Quickshell.execDetached([ - "notify-send", - "-u", - dbusUrgency, - "-a", - "DMS", - "-i", - icon, - title, - message - ]); + Quickshell.execDetached(["notify-send", "-u", dbusUrgency, "-a", "DMS", "-i", icon, title, message]); } else if (urgency === "critical") { ToastService.showError(title, message, "", icon); } else if (urgency === "warning") { @@ -463,16 +431,7 @@ Singleton { if (!_hasNotifiedChargeLimit && SettingsData.batteryNotifyChargeLimit) { _hasNotifiedChargeLimit = true; - sendAlert( - I18n.tr("Charge Limit Reached"), - I18n.tr( - "Battery has charged to your set limit of %1%", - "charge limit notification body, %1 is the limit percentage" - ).arg(SettingsData.batteryChargeLimit), - "info", - "material:battery_profile", - SettingsData.batteryChargeLimitNotificationType - ); + sendAlert(I18n.tr("Charge Limit Reached"), I18n.tr("Battery has charged to your set limit of %1%", "charge limit notification body, %1 is the limit percentage").arg(SettingsData.batteryChargeLimit), "info", "material:battery_profile", SettingsData.batteryChargeLimitNotificationType); } } else if (!isCharging || batteryLevel < SettingsData.batteryChargeLimit - 2) { _hasNotifiedChargeLimit = false; @@ -489,16 +448,7 @@ Singleton { if (!_hasNotifiedCriticalBattery && SettingsData.batteryNotifyCritical) { _hasNotifiedCriticalBattery = true; - sendAlert( - I18n.tr("Critical Battery"), - I18n.tr( - "Battery is at %1% - Connect charger immediately!", - "critical battery notification body, %1 is the battery percentage" - ).arg(batteryLevel), - "critical", - "material:battery_alert", - SettingsData.batteryCriticalNotificationType - ); + sendAlert(I18n.tr("Critical Battery"), I18n.tr("Battery is at %1% - Connect charger immediately!", "critical battery notification body, %1 is the battery percentage").arg(batteryLevel), "critical", "material:battery_alert", SettingsData.batteryCriticalNotificationType); } return; @@ -512,16 +462,7 @@ Singleton { if (!_hasNotifiedLowBattery && SettingsData.batteryNotifyLow) { _hasNotifiedLowBattery = true; - sendAlert( - I18n.tr("Low Battery"), - I18n.tr( - "Battery is at %1% - Consider charging soon", - "low battery notification body, %1 is the battery percentage" - ).arg(batteryLevel), - "warning", - "material:battery_0_bar", - SettingsData.batteryLowNotificationType - ); + sendAlert(I18n.tr("Low Battery"), I18n.tr("Battery is at %1% - Consider charging soon", "low battery notification body, %1 is the battery percentage").arg(batteryLevel), "warning", "material:battery_0_bar", SettingsData.batteryLowNotificationType); } if (SettingsData.batteryAutoPowerSaver && PowerProfileWatcher.available) { @@ -536,13 +477,9 @@ Singleton { onIsChargingChanged: { // Reset average when switching states - _smoothedChangeRate = (_hasKnownChargingState && changeRate > 0) - ? changeRate - : 0; + _smoothedChangeRate = (_hasKnownChargingState && changeRate > 0) ? changeRate : 0; - _lastRateSampleTime = _smoothedChangeRate > 0 - ? Date.now() - : 0; + _lastRateSampleTime = _smoothedChangeRate > 0 ? Date.now() : 0; if (isCharging) { _hasNotifiedLowBattery = false; @@ -569,11 +506,9 @@ Singleton { applyPowerProfile(); if (isPluggedIn) { - const dismissLow = SettingsData.batteryLowNotificationType === 1 - && SettingsData.notificationTimeoutNormal === 0; + const dismissLow = SettingsData.batteryLowNotificationType === 1 && SettingsData.notificationTimeoutNormal === 0; - const dismissCritical = SettingsData.batteryCriticalNotificationType === 1 - && SettingsData.notificationTimeoutCritical === 0; + const dismissCritical = SettingsData.batteryCriticalNotificationType === 1 && SettingsData.notificationTimeoutCritical === 0; if (dismissLow || dismissCritical) { const lowSummary = I18n.tr("Low Battery"); @@ -585,8 +520,7 @@ Singleton { const summary = w.notification.summary; - if ((dismissLow && summary === lowSummary) - || (dismissCritical && summary === criticalSummary)) { + if ((dismissLow && summary === lowSummary) || (dismissCritical && summary === criticalSummary)) { NotificationService.dismissNotification(w); } } @@ -646,9 +580,7 @@ Singleton { return ""; const magnitude = Math.abs(rate); - const value = (compact || magnitude >= 10) - ? Math.round(magnitude).toString() - : magnitude.toFixed(1); + const value = (compact || magnitude >= 10) ? Math.round(magnitude).toString() : magnitude.toFixed(1); return `${rate > 0 ? "+" : "-"}${value}W`; } @@ -705,17 +637,12 @@ Singleton { if (usePreferred && preferredDeviceReady && preferredDevice.healthSupported) return `${Math.round(preferredDevice.healthPercentage)}%`; - const validBatteries = readyBatteries.filter( - b => b.healthSupported && b.healthPercentage > 0 - ); + const validBatteries = readyBatteries.filter(b => b.healthSupported && b.healthPercentage > 0); if (validBatteries.length === 0) return "N/A"; - const avgHealth = validBatteries.reduce( - (sum, b) => sum + b.healthPercentage, - 0 - ) / validBatteries.length; + const avgHealth = validBatteries.reduce((sum, b) => sum + b.healthPercentage, 0) / validBatteries.length; return `${Math.round(avgHealth)}%`; } @@ -779,18 +706,14 @@ Singleton { if (isCharging) return I18n.tr("Charging", "battery status"); - return isPluggedIn - ? I18n.tr("Plugged in", "battery status") - : I18n.tr("Discharging", "battery status"); + return isPluggedIn ? I18n.tr("Plugged in", "battery status") : I18n.tr("Discharging", "battery status"); } if (stateKnownBatteries.length === 0) { if (isCharging) return I18n.tr("Charging", "battery status"); - return isPluggedIn - ? I18n.tr("Plugged in", "battery status") - : I18n.tr("Discharging", "battery status"); + return isPluggedIn ? I18n.tr("Plugged in", "battery status") : I18n.tr("Discharging", "battery status"); } if (isCharging && !stateKnownBatteries.some(b => b.changeRate > 0)) @@ -801,37 +724,21 @@ Singleton { if (states.every(s => s === states[0])) return translateBatteryState(states[0]); - return isCharging - ? I18n.tr("Charging", "battery status") - : (isPluggedIn - ? I18n.tr("Plugged in", "battery status") - : I18n.tr("Discharging", "battery status")); + return isCharging ? I18n.tr("Charging", "battery status") : (isPluggedIn ? I18n.tr("Plugged in", "battery status") : I18n.tr("Discharging", "battery status")); } readonly property bool suggestPowerSaver: false - readonly property var peripheralDevices: UPower.devices.values - .filter(dev => dev - && dev.ready - && !dev.isLaptopBattery - && peripheralIcon(dev.type) !== "") - .map(dev => ({ - "name": dev.model || UPowerDeviceType.toString(dev.type), - "percentage": Math.round(dev.percentage * 100), - "type": dev.type, - "icon": peripheralIcon(dev.type), - "charging": dev.state === UPowerDeviceState.Charging - })) + readonly property var peripheralDevices: UPower.devices.values.filter(dev => dev && dev.ready && !dev.isLaptopBattery && peripheralIcon(dev.type) !== "").map(dev => ({ + "name": dev.model || UPowerDeviceType.toString(dev.type), + "percentage": Math.round(dev.percentage * 100), + "type": dev.type, + "icon": peripheralIcon(dev.type), + "charging": dev.state === UPowerDeviceState.Charging + })) readonly property var bluetoothDevices: { - const bluetoothTypes = [ - UPowerDeviceType.BluetoothGeneric, - UPowerDeviceType.Headphones, - UPowerDeviceType.Headset, - UPowerDeviceType.Keyboard, - UPowerDeviceType.Mouse, - UPowerDeviceType.Speakers - ]; + const bluetoothTypes = [UPowerDeviceType.BluetoothGeneric, UPowerDeviceType.Headphones, UPowerDeviceType.Headset, UPowerDeviceType.Keyboard, UPowerDeviceType.Mouse, UPowerDeviceType.Speakers]; return peripheralDevices.filter(dev => bluetoothTypes.includes(dev.type)); } @@ -873,13 +780,9 @@ Singleton { if (!batteryAvailable) return 0; - const rate = _smoothedChangeRate > 0 - ? _smoothedChangeRate - : changeRate; + const rate = _smoothedChangeRate > 0 ? _smoothedChangeRate : changeRate; - const totalTime = isCharging - ? ((batteryCapacity - batteryEnergy) / rate) - : (batteryEnergy / rate); + const totalTime = isCharging ? ((batteryCapacity - batteryEnergy) / rate) : (batteryEnergy / rate); const seconds = Math.abs(totalTime * 3600); @@ -893,9 +796,7 @@ Singleton { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); - return hours > 0 - ? I18n.tr("%1h %2m", "battery time remaining").arg(hours).arg(minutes) - : I18n.tr("%1m", "battery time remaining").arg(minutes); + return hours > 0 ? I18n.tr("%1h %2m", "battery time remaining").arg(hours).arg(minutes) : I18n.tr("%1m", "battery time remaining").arg(minutes); } function formatTimeRemaining() { @@ -916,10 +817,7 @@ Singleton { const target = new Date(Date.now() + seconds * 1000); const use24Hour = SettingsData.use24HourClock !== false; - return target.toLocaleTimeString( - Qt.locale(), - use24Hour ? "HH:mm" : "h:mm AP" - ); + return target.toLocaleTimeString(Qt.locale(), use24Hour ? "HH:mm" : "h:mm AP"); } function getBatteryIcon() {