nova-shell/modules/Battery.qml

72 lines
3 KiB
QML

import QtQuick
import Quickshell.Io
import Quickshell.Services.UPower
import "." as M
M.BarSection {
id: root
spacing: M.Theme.moduleSpacing
opacity: M.Modules.battery && (UPower.displayDevice?.isLaptopBattery ?? false) ? 1 : 0
visible: opacity > 0
tooltip: {
const state = root.charging ? "Charging" : "Discharging";
const t = root.charging ? root.dev?.timeToFull : root.dev?.timeToEmpty;
const mins = t ? Math.round(t / 60) : 0;
const timeStr = mins > 0 ? "\n" + Math.floor(mins / 60) + "h " + (mins % 60) + "m " + (root.charging ? "until full" : "remaining") : "";
return state + " \u2014 " + Math.round(root.pct) + "%" + timeStr;
}
readonly property var dev: UPower.displayDevice
readonly property real pct: (dev?.percentage ?? 0) * 100
readonly property bool charging: dev?.state === UPowerDeviceState.Charging
readonly property bool _critical: pct < 15 && !charging
property color _stateColor: charging ? M.Theme.base0B : _critical ? M.Theme.base09 : pct < 30 ? M.Theme.base0A : M.Theme.base08
property real _blinkOpacity: 1
SequentialAnimation {
running: root._critical
loops: Animation.Infinite
NumberAnimation { target: root; property: "_blinkOpacity"; to: 0.2; duration: 400; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "_blinkOpacity"; to: 1; duration: 400; easing.type: Easing.InOutQuad }
onRunningChanged: if (!running) root._blinkOpacity = 1
}
property bool _warnSent: false
property bool _critSent: false
onChargingChanged: { _warnSent = false; _critSent = false; }
onPctChanged: {
if (charging) return;
if (pct < 15 && !_critSent) {
_critSent = true; _warnSent = true;
_notif.command = ["notify-send", "--urgency=critical", "--icon=battery-low", "--category=device", "Very Low Battery", "Connect to power now!"];
_notif.running = true;
} else if (pct < 25 && !_warnSent) {
_warnSent = true;
_notif.command = ["notify-send", "--icon=battery-caution", "--category=device", "Low Battery"];
_notif.running = true;
}
}
Process { id: _notif }
M.BarIcon {
icon: {
if (root.charging)
return "\uDB80\uDC84";
const icons = ["\uDB80\uDC8E", "\uDB80\uDC7A", "\uDB80\uDC7B", "\uDB80\uDC7C", "\uDB80\uDC7D", "\uDB80\uDC7E", "\uDB80\uDC7F", "\uDB80\uDC80", "\uDB80\uDC81", "\uDB80\uDC82", "\uDB85\uDFE2"];
return icons[Math.min(10, Math.floor(root.pct / 10))];
}
color: root._stateColor
opacity: root._blinkOpacity
font.pixelSize: M.Theme.fontSize + 2
anchors.verticalCenter: parent.verticalCenter
}
M.BarLabel {
label: Math.round(root.pct) + "%"
minText: "100%"
color: root._stateColor
opacity: root._blinkOpacity
anchors.verticalCenter: parent.verticalCenter
}
}