From ae9a80c1f012686c1f65fce7ce76fdd1b77b0b71 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Wed, 23 Sep 2026 21:08:35 +0200 Subject: ... --- .../shell/quickshell/BatteryWidget.qml | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 accounts/gkleen@skadhi/shell/quickshell/BatteryWidget.qml (limited to 'accounts/gkleen@skadhi/shell/quickshell/BatteryWidget.qml') diff --git a/accounts/gkleen@skadhi/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@skadhi/shell/quickshell/BatteryWidget.qml new file mode 100644 index 00000000..f465fa20 --- /dev/null +++ b/accounts/gkleen@skadhi/shell/quickshell/BatteryWidget.qml @@ -0,0 +1,139 @@ +import QtQuick +import Quickshell +import Quickshell.Widgets +import Quickshell.Services.UPower + +Item { + id: root + + height: parent.height + width: batteryIcon.width + 8 + anchors.verticalCenter: parent.verticalCenter + + property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery) + + WrapperMouseArea { + id: widgetMouseArea + + anchors.fill: parent + + hoverEnabled: true + + Item { + anchors.fill: parent + + MaterialDesignIcon { + id: batteryIcon + + implicitSize: 14 + anchors.centerIn: parent + + icon: { + if (!root.batteryDevice?.ready) + return "battery-unknown"; + + if (root.batteryDevice.state == UPowerDeviceState.FullyCharged) + return "power-plug-battery"; + + const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10)); + if (root.batteryDevice.state == UPowerDeviceState.Charging) + return `battery-charging-${perdec}`; + if (perdec == 100) + return "battery"; + return `battery-${perdec}`; + } + color: { + if (!root.batteryDevice?.ready) + return "#555"; + + if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60) + return "#f2201f"; + if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60) + return "#f28a21"; + if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) + return "#fff"; + return "#555"; + } + } + } + } + + PopupWindow { + id: tooltip + + property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse + + anchor { + item: widgetMouseArea + edges: Edges.Bottom | Edges.Left + } + visible: false + + onNextVisibleChanged: hangTimer.restart() + + Timer { + id: hangTimer + interval: 100 + onTriggered: tooltip.visible = tooltip.nextVisible + } + + implicitWidth: widgetTooltipText.contentWidth + 16 + implicitHeight: widgetTooltipText.contentHeight + 16 + color: "black" + + WrapperMouseArea { + id: tooltipMouseArea + + hoverEnabled: true + enabled: true + + anchors.fill: parent + + Item { + anchors.fill: parent + + Text { + id: widgetTooltipText + + anchors.centerIn: parent + + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: { + if (!root.batteryDevice?.ready) + return "Not ready"; + + const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); + var outStr = stateStr; + if (root.batteryDevice.state !== UPowerDeviceState.FullyCharged) + outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; + + function formatTime(t) { + var res = ""; + for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { + if (t < unit.v) + continue; + res += Math.floor(t / unit.v) + unit.s; + t %= unit.v; + } + return res; + } + if (root.batteryDevice.timeToEmpty != 0) { + const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); + if (tStr) + outStr += " " + tStr; + } else if (root.batteryDevice.timeToFull != 0) { + const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); + if (tStr) + outStr += " " + tStr; + } + + return outStr; + } + } + } + } + } +} -- cgit v1.2.3