Add notification when battery is low, fix some warnings
This commit is contained in:
parent
d2b202c25f
commit
c6683712a4
5 changed files with 52 additions and 51 deletions
|
|
@ -8,21 +8,57 @@ import qs.Widgets
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ShellScreen screen
|
property ShellScreen screen
|
||||||
property real scaling: ScalingService.scale(screen)
|
property real scaling: ScalingService.scale(screen)
|
||||||
|
|
||||||
implicitWidth: pill.width
|
implicitWidth: pill.width
|
||||||
implicitHeight: pill.height
|
implicitHeight: pill.height
|
||||||
|
|
||||||
|
// Track if we've already notified to avoid spam
|
||||||
|
property bool hasNotifiedLowBattery: false
|
||||||
|
|
||||||
|
// Helper to evaluate and possibly notify
|
||||||
|
function maybeNotify(percent, charging) {
|
||||||
|
const p = Math.round(percent)
|
||||||
|
if (!charging && p <= 15 && !root.hasNotifiedLowBattery) {
|
||||||
|
Quickshell.execDetached(
|
||||||
|
["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`])
|
||||||
|
root.hasNotifiedLowBattery = true
|
||||||
|
}
|
||||||
|
if (p > 20 || charging) {
|
||||||
|
root.hasNotifiedLowBattery = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for battery changes
|
||||||
|
Connections {
|
||||||
|
target: UPower.displayDevice
|
||||||
|
function onPercentageChanged() {
|
||||||
|
let battery = UPower.displayDevice
|
||||||
|
let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent
|
||||||
|
let percent = isReady ? (battery.percentage * 100) : 0
|
||||||
|
let charging = isReady ? battery.state === UPowerDeviceState.Charging : false
|
||||||
|
|
||||||
|
root.maybeNotify(percent, charging)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStateChanged() {
|
||||||
|
let battery = UPower.displayDevice
|
||||||
|
let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent
|
||||||
|
let charging = isReady ? battery.state === UPowerDeviceState.Charging : false
|
||||||
|
|
||||||
|
// Reset notification flag when charging starts
|
||||||
|
if (charging) {
|
||||||
|
root.hasNotifiedLowBattery = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NPill {
|
NPill {
|
||||||
id: pill
|
id: pill
|
||||||
|
|
||||||
// Test mode
|
// Test mode
|
||||||
property bool testMode: false
|
property bool testMode: false
|
||||||
property int testPercent: 49
|
property int testPercent: 20
|
||||||
property bool testCharging: false
|
property bool testCharging: false
|
||||||
|
|
||||||
property var battery: UPower.displayDevice
|
property var battery: UPower.displayDevice
|
||||||
property bool isReady: testMode ? true : (battery && battery.ready && battery.isLaptopBattery && battery.isPresent)
|
property bool isReady: testMode ? true : (battery && battery.ready && battery.isLaptopBattery && battery.isPresent)
|
||||||
property real percent: testMode ? testPercent : (isReady ? (battery.percentage * 100) : 0)
|
property real percent: testMode ? testPercent : (isReady ? (battery.percentage * 100) : 0)
|
||||||
|
|
@ -30,16 +66,12 @@ Item {
|
||||||
|
|
||||||
// Choose icon based on charge and charging state
|
// Choose icon based on charge and charging state
|
||||||
function batteryIcon() {
|
function batteryIcon() {
|
||||||
|
|
||||||
if (!isReady || !battery.isLaptopBattery)
|
if (!isReady || !battery.isLaptopBattery)
|
||||||
return "battery_android_alert"
|
return "battery_android_alert"
|
||||||
|
|
||||||
if (charging)
|
if (charging)
|
||||||
return "battery_android_bolt"
|
return "battery_android_bolt"
|
||||||
|
|
||||||
if (percent >= 95)
|
if (percent >= 95)
|
||||||
return "battery_android_full"
|
return "battery_android_full"
|
||||||
|
|
||||||
// Hardcoded battery symbols
|
// Hardcoded battery symbols
|
||||||
if (percent >= 85)
|
if (percent >= 85)
|
||||||
return "battery_android_6"
|
return "battery_android_6"
|
||||||
|
|
@ -60,28 +92,26 @@ Item {
|
||||||
icon: batteryIcon()
|
icon: batteryIcon()
|
||||||
text: (isReady && battery.isLaptopBattery) ? Math.round(percent) + "%" : "-"
|
text: (isReady && battery.isLaptopBattery) ? Math.round(percent) + "%" : "-"
|
||||||
textColor: charging ? Color.mPrimary : Color.mOnSurface
|
textColor: charging ? Color.mPrimary : Color.mOnSurface
|
||||||
forceOpen: isReady && battery.isLaptopBattery && Settings.data.bar.alwaysShowBatteryPercentage
|
iconCircleColor: Color.mPrimary
|
||||||
disableOpen: (!isReady || !battery.isLaptopBattery)
|
collapsedIconColor: Color.mOnSurface
|
||||||
|
autoHide: false
|
||||||
|
forceOpen: isReady && (testMode || battery.isLaptopBattery) && Settings.data.bar.alwaysShowBatteryPercentage
|
||||||
|
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
|
||||||
tooltipText: {
|
tooltipText: {
|
||||||
let lines = []
|
let lines = []
|
||||||
|
|
||||||
if (testMode) {
|
if (testMode) {
|
||||||
lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(12345))
|
lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(12345))
|
||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isReady || !battery.isLaptopBattery) {
|
if (!isReady || !battery.isLaptopBattery) {
|
||||||
return "No Battery Detected"
|
return "No Battery Detected"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (battery.timeToEmpty > 0) {
|
if (battery.timeToEmpty > 0) {
|
||||||
lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(battery.timeToEmpty))
|
lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(battery.timeToEmpty))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (battery.timeToFull > 0) {
|
if (battery.timeToFull > 0) {
|
||||||
lines.push("Time Until Full: " + Time.formatVagueHumanReadableDuration(battery.timeToFull))
|
lines.push("Time Until Full: " + Time.formatVagueHumanReadableDuration(battery.timeToFull))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (battery.changeRate !== undefined) {
|
if (battery.changeRate !== undefined) {
|
||||||
const rate = battery.changeRate
|
const rate = battery.changeRate
|
||||||
if (rate > 0) {
|
if (rate > 0) {
|
||||||
|
|
@ -95,7 +125,6 @@ Item {
|
||||||
} else {
|
} else {
|
||||||
lines.push(charging ? "Charging" : "Discharging")
|
lines.push(charging ? "Charging" : "Discharging")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (battery.healthPercentage !== undefined && battery.healthPercentage > 0) {
|
if (battery.healthPercentage !== undefined && battery.healthPercentage > 0) {
|
||||||
lines.push("Health: " + Math.round(battery.healthPercentage) + "%")
|
lines.push("Health: " + Math.round(battery.healthPercentage) + "%")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,38 +203,7 @@ Variants {
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification actions
|
// Actions removed
|
||||||
RowLayout {
|
|
||||||
visible: model.actions && model.actions.length > 0
|
|
||||||
spacing: Style.marginXS * scaling
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: Style.marginS * scaling
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: model.actions || []
|
|
||||||
delegate: NPill {
|
|
||||||
text: modelData.text || modelData.identifier || "Action"
|
|
||||||
icon: modelData.identifier || ""
|
|
||||||
tooltipText: modelData.text || modelData.identifier || "Action"
|
|
||||||
sizeMultiplier: 0.7
|
|
||||||
Layout.fillWidth: true
|
|
||||||
forceOpen: true
|
|
||||||
|
|
||||||
// Style action buttons differently
|
|
||||||
pillColor: Color.mPrimary
|
|
||||||
textColor: Color.mOnPrimary
|
|
||||||
iconCircleColor: Color.mPrimary
|
|
||||||
iconTextColor: Color.mOnPrimary
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
// Invoke the notification action
|
|
||||||
modelData.invoke()
|
|
||||||
// Animate out the notification after action
|
|
||||||
animateOut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NIconButton {
|
NIconButton {
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@ Singleton {
|
||||||
Logger.log("Wallpapers", "Listing wallpapers")
|
Logger.log("Wallpapers", "Listing wallpapers")
|
||||||
scanning = true
|
scanning = true
|
||||||
wallpaperList = []
|
wallpaperList = []
|
||||||
// Unsetting, then setting the folder will re-trigger the parsing!
|
// Set the folder directly to avoid model reset issues
|
||||||
folderModel.folder = ""
|
|
||||||
folderModel.folder = "file://" + (Settings.data.wallpaper.directory !== undefined ? Settings.data.wallpaper.directory : "")
|
folderModel.folder = "file://" + (Settings.data.wallpaper.directory !== undefined ? Settings.data.wallpaper.directory : "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,7 @@ Text {
|
||||||
font.pointSize: Style.fontSizeM * scaling
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
font.weight: Style.fontWeightMedium
|
font.weight: Style.fontWeightMedium
|
||||||
color: Color.mOnSurface
|
color: Color.mOnSurface
|
||||||
|
renderType: Text.QtRendering
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
font.kerning: true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
import qs.Commons
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue