Files
dotfiles/quickshell/services/Battery.qml
T
nvrlandClaude Opus 5 e221445a51 theme: Catppuccin Mocha blue/pink variant of the Persona rice
Keeps the Takemi geometry — the lean, the chamfer, the slabs — and swaps
the palette wholesale. Mocha carries the ground; blue is the accent for
anything live (focus, selection, borders, hover) and pink is the alarm
for anything destructive or failed, so one hue no longer has to mean
both 'active' and 'wrong'.

Destructive controls rest as a pink keyline and fill solid only when
hovered or selected: Mocha's pink is a pastel, and a resting slab of it
reads as washed-out red rather than as a warning. The load ramp climbs
text -> blue -> pink for the same reason, instead of fading straight
through a muddy mid-tone.

Surfaces covered: quickshell, Hyprland borders, alacritty, fish, dunst,
and nvim (via the catppuccin plugin, submodule branch catppuccin-mocha).
GTK 3/4, Qt and xsettingsd move to catppuccin-mocha-blue and are now
tracked, since nothing else makes non-shell apps match the rice; the
previous Takemi copies are in theme-repair-backups/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:11:02 +02:00

102 lines
3.7 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Services.UPower
import "root:/config"
import "root:/services"
// Battery and power-profile state. UPower gives live percentage and time
// estimates; Sys.powerDetail adds the TLP/AC line fluxo reports.
Singleton {
id: root
readonly property UPowerDevice device: UPower.displayDevice
readonly property bool available: device !== null && device.isLaptopBattery && device.isPresent
readonly property bool onBattery: UPower.onBattery
readonly property real percent: device?.percentage !== undefined ? device.percentage * 100 : 0
readonly property int state: device?.state ?? UPowerDeviceState.Unknown
readonly property bool charging: state === UPowerDeviceState.Charging
|| state === UPowerDeviceState.PendingCharge
readonly property bool full: state === UPowerDeviceState.FullyCharged
readonly property real changeRate: device?.changeRate ?? 0 // watts
readonly property real health: device?.healthSupported ? (device?.healthPercentage ?? 0) : 0
readonly property bool low: available && !charging && percent <= 20
readonly property bool critical: available && !charging && percent <= 10
readonly property real secondsLeft: charging
? (device?.timeToFull ?? 0)
: (device?.timeToEmpty ?? 0)
readonly property string timeLabel: {
if (root.full) return "Full";
if (!(root.secondsLeft > 0)) return charging ? "Charging" : "Estimating…";
const mins = Math.round(root.secondsLeft / 60);
const h = Math.floor(mins / 60);
const m = mins % 60;
const dur = h > 0 ? h + "h " + m + "m" : m + "m";
return root.charging ? dur + " to full" : dur + " left";
}
readonly property string detail: Sys.powerDetail
// Ten-step battery glyph ramp, plus the charging variants.
readonly property string icon: {
if (!root.available) return "󰚥";
const p = Math.max(0, Math.min(100, root.percent));
const step = Math.min(10, Math.floor(p / 10));
if (root.charging) {
const charge = ["󰢜", "󰂆", "󰂇", "󰂈", "󰢝", "󰂉", "󰢞", "󰂊", "󰂋", "󰂅", "󰂅"];
return charge[step];
}
const drain = ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹", "󰁹"];
return drain[step];
}
readonly property color tint: {
if (root.charging || root.full) return Theme.ok;
if (root.critical) return Theme.alarm;
if (root.low) return Theme.warn;
return Theme.primary;
}
// ------------------------------------------------------- power profiles
readonly property bool hasPerformance: PowerProfiles.hasPerformanceProfile
readonly property int profile: PowerProfiles.profile
readonly property string profileLabel: {
switch (root.profile) {
case PowerProfile.PowerSaver: return "Power Saver";
case PowerProfile.Performance: return "Performance";
default: return "Balanced";
}
}
readonly property string profileIcon: {
switch (root.profile) {
case PowerProfile.PowerSaver: return "󰌪";
case PowerProfile.Performance: return "󰓅";
default: return "󰾅";
}
}
function setProfile(p: int) {
PowerProfiles.profile = p;
}
function cycleProfile() {
if (root.profile === PowerProfile.PowerSaver) {
root.setProfile(PowerProfile.Balanced);
} else if (root.profile === PowerProfile.Balanced
&& PowerProfiles.hasPerformanceProfile) {
root.setProfile(PowerProfile.Performance);
} else {
root.setProfile(PowerProfile.PowerSaver);
}
}
}