Fix power panel discharge state

This commit is contained in:
David Heinemeier Hansson
2026-06-06 18:01:14 +02:00
parent b83505d738
commit 740f4e2c82
4 changed files with 24 additions and 15 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ fi
charge_holding=false
if [[ $ac_online == "true" && -n $threshold_end ]]; then
if [[ $state == "discharging" || $state == "pending-charge" ]]; then
if [[ $state == "pending-charge" ]]; then
charge_holding=true
elif [[ $state == "fully-charged" ]] && (( percentage < 99 )); then
charge_holding=true
+4 -2
View File
@@ -55,7 +55,8 @@ function chargeThresholdActive(device, onBattery, states) {
if (!(d && d.isPresent && !onBattery)) return false
var fraction = batteryFraction(d)
if (d.state === s.Discharging || d.state === s.PendingCharge) return true
if (d.state === s.Discharging) return false
if (d.state === s.PendingCharge) return true
if (d.state === s.FullyCharged && fraction < 0.99) return true
if (d.state !== s.Charging || fraction >= 0.99) return false
@@ -74,6 +75,7 @@ function batteryIcon(device, onBattery, states) {
if (threshold) return defaultIcons[index]
if (d.state === states.FullyCharged) return "󰂅"
if (d.state === states.Charging) return chargingIcons[index]
if (d.state === states.Discharging) return defaultIcons[index]
if (!onBattery) return ""
return defaultIcons[index]
}
@@ -85,7 +87,7 @@ function modeLabel(device, onBattery, states) {
var percentage = d.isPresent ? d.percentage : 0
if (chargeThresholdActive(d, onBattery, states)) return "Threshold"
if (!onBattery && percentage >= 1) return "Fully charged"
if (onBattery) return "On battery"
if (onBattery || d.state === states.Discharging) return "On battery"
return "Charging"
}
+17 -12
View File
@@ -41,12 +41,12 @@ Panel {
function batteryIcon() {
var device = UPower.displayDevice
return Model.batteryIcon(device, UPower.onBattery, upowerStates())
return Model.batteryIcon(device, root.discharging, upowerStates())
}
function modeLabel() {
var device = UPower.displayDevice
return Model.modeLabel(device, UPower.onBattery, upowerStates())
return Model.modeLabel(device, root.discharging, upowerStates())
}
function profileIcon(name) {
@@ -57,11 +57,15 @@ Panel {
var device = UPower.displayDevice
return device && device.isPresent && device.state === UPowerDeviceState.FullyCharged && !root.chargeThresholdActive
}
readonly property bool discharging: {
var device = UPower.displayDevice
return !!(device && device.isPresent && (UPower.onBattery || device.state === UPowerDeviceState.Discharging))
}
readonly property bool chargeThresholdActive: {
var device = UPower.displayDevice
return Model.chargeThresholdActive(device, UPower.onBattery, upowerStates())
return Model.chargeThresholdActive(device, root.discharging, upowerStates())
}
readonly property bool batteryFull: fullyCharged || (!UPower.onBattery && batteryFraction >= 1)
readonly property bool batteryFull: fullyCharged || (!root.discharging && batteryFraction >= 1)
readonly property bool batteryFlowIdle: batteryFull || chargeThresholdActive
// 0..1 charge level, used by the visual progress bar.
@@ -70,14 +74,12 @@ Panel {
return Model.batteryFraction(d)
}
readonly property bool batteryLow: UPower.onBattery && batteryFraction > 0 && batteryFraction <= 0.2
readonly property bool charging: {
var d = UPower.displayDevice
return d && d.isPresent && d.state === UPowerDeviceState.Charging && !root.chargeThresholdActive
}
readonly property color batteryFillColor: {
if (batteryLow) return Color.urgent
return root.bar ? root.bar.foreground : Color.foreground
}
@@ -111,7 +113,7 @@ Panel {
readonly property var activePhrases: {
if (fullyCharged) return []
if (charging) return chargingPhrases
if (UPower.onBattery || chargeThresholdActive) return onBatteryPhrases
if (discharging) return onBatteryPhrases
return []
}
readonly property bool rotatingPhrases: activePhrases.length > 0
@@ -254,7 +256,6 @@ Panel {
text: root.batteryIcon()
fixedWidth: root.bar && root.bar.vertical ? -1 : Style.space(27)
fixedHeight: root.bar && root.bar.vertical ? Style.space(26) : -1
active: root.batteryPresent && UPower.displayDevice.percentage <= 0.2 && UPower.onBattery
tooltipText: ""
onPressed: function(b) { if (root.batteryPresent) root.toggle() }
}
@@ -296,7 +297,7 @@ Panel {
Text {
id: heroIcon
text: root.batteryIcon()
color: root.batteryLow ? Color.urgent : root.bar.foreground
color: root.bar.foreground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.display
anchors.left: parent.left
@@ -340,7 +341,7 @@ Panel {
Text {
id: heroPercent
text: root.batteryInfo.percentage || "—"
color: root.batteryLow ? Color.urgent : root.bar.foreground
color: root.bar.foreground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.displayLarge
font.bold: true
@@ -408,17 +409,21 @@ Panel {
width: (parent.width - parent.spacing) / 2
spacing: Style.spacing.labelGap
InfoPair {
label: root.chargeThresholdActive ? "Charge limit" : (UPower.onBattery ? "Time left" : "Time to full")
label: root.chargeThresholdActive ? "Charge limit" : (root.discharging ? "Time left" : "Time to full")
value: root.chargeThresholdActive ? (root.batteryInfo.threshold || "-") : (root.batteryFlowIdle ? "-" : (root.batteryInfo.time || "—"))
}
InfoPair {
label: root.chargeThresholdActive ? "Battery state" : (UPower.onBattery ? "Discharging" : "Charging")
label: root.chargeThresholdActive ? "Battery state" : (root.discharging ? "Discharging" : "Charging")
value: root.chargeThresholdActive ? "Holding" : (root.batteryFull ? "-" : (root.batteryInfo.rate || ""))
}
}
}
// ---------- Power profile picker ----------
PanelSeparator {
foreground: root.bar.foreground
}
Column {
width: parent.width
spacing: Style.space(10)
+2
View File
@@ -24,7 +24,9 @@ assertEqual(power.batteryFraction({ isPresent: true, percentage: 1.5 }), 1, 'pow
assert(power.chargeThresholdActive({ isPresent: true, percentage: 0.8, state: states.PendingCharge }, false, states), 'power detects threshold by pending charge state')
assert(power.chargeThresholdActive({ isPresent: true, percentage: 0.8, state: states.Charging, changeRate: 0.1, timeToFull: 120 }, false, states), 'power detects threshold by stalled charging')
assert(!power.chargeThresholdActive({ isPresent: true, percentage: 0.8, state: states.Charging, changeRate: 1.0, timeToFull: 120 }, false, states), 'power does not flag active charging as threshold')
assert(!power.chargeThresholdActive({ isPresent: true, percentage: 0.5, state: states.Discharging }, false, states), 'power does not flag discharging as threshold')
assertEqual(power.modeLabel({ isPresent: true, percentage: 1, state: states.FullyCharged }, false, states), 'Fully charged', 'power labels full battery')
assertEqual(power.modeLabel({ isPresent: true, percentage: 0.5, state: states.Discharging }, true, states), 'On battery', 'power labels battery mode')
assertEqual(power.modeLabel({ isPresent: true, percentage: 0.5, state: states.Discharging }, false, states), 'On battery', 'power labels discharging battery mode')
assert(power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Charging }, false, states).length > 0, 'power maps battery icons')
JS