diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index 549ba40..5831ab9 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -73,6 +73,7 @@ PanelWindow { // TODO: Add bluetooth, network, audio, power, maybe HW monitor Tray{} + Battery{} Network {} IdleButton {} PowerButton {} diff --git a/modules/bar/states/Battery/BatteryState.qml b/modules/bar/states/Battery/BatteryState.qml new file mode 100644 index 0000000..bb5e73a --- /dev/null +++ b/modules/bar/states/Battery/BatteryState.qml @@ -0,0 +1,83 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Io + +Item { + property real percentage: 0 + property bool charging: false + property real energy: 0 + property real voltage: 0 + property int charge_cycle: 0 + property real time_to_empty: 0 + property real energy_rate: 0 + + function parseUpower(line) { + line = line.trim() + + if (line.startsWith("percentage:")) { + percentage = parseInt(line.split(":")[1]) + // console.log("percentage =", percentage) + } + + else if (line.startsWith("state:")) { + charging = !line.includes("discharging") + // console.log("charging =", charging) + } + + else if (line.startsWith("energy:")) { + energy = parseFloat(line.split(":")[1]) + // console.log("energy =", energy) + } + + else if (line.startsWith("voltage:")) { + voltage = parseFloat(line.split(":")[1]) + // console.log("voltage =", voltage) + } + + else if (line.startsWith("energy-rate:")) { + energy_rate = parseFloat(line.split(":")[1]) + // console.log("energy_rate =", energy_rate) + } + + else if (line.startsWith("charge-cycles:")) { + charge_cycle = parseInt(line.split(":")[1]) + // console.log("charge_cycle =", charge_cycle) + } + + else if (line.startsWith("time to empty:")) { + time_to_empty = parseFloat(line.split(":")[1]) + // console.log("time_to_empty =", time_to_empty) + } + } + + Process { + id: upowerProc + command: ["bash", "-c", "upower -i $(upower -e | grep BAT)"] + + stdout: SplitParser { + onRead: function(data) { + parseUpower(data) + // console.log("battery available:", data) + } + } + } + + Timer { + id: pollTimer + interval: 1000 + running: false + repeat: true + triggeredOnStart: true + onTriggered: upowerProc.running = true + } + + function start() { + pollTimer.start() + upowerProc.running = true + } + + function stop() { + pollTimer.stop() + } +} \ No newline at end of file diff --git a/modules/bar/states/Battery/qmldir b/modules/bar/states/Battery/qmldir new file mode 100644 index 0000000..1577d8a --- /dev/null +++ b/modules/bar/states/Battery/qmldir @@ -0,0 +1 @@ +singleton BatteryState 1.0 BatteryState.qml \ No newline at end of file diff --git a/modules/bar/widgets/Battery.qml b/modules/bar/widgets/Battery.qml new file mode 100644 index 0000000..59be547 --- /dev/null +++ b/modules/bar/widgets/Battery.qml @@ -0,0 +1,104 @@ +import QtQuick +import Quickshell +import Quickshell.Hyprland +import Quickshell.Io +import "../../../colors" +import "../states/Battery" + +MouseArea { + id: batteryButton + width: clicked ? Math.max(batteryPercentageText.width + parent.spacing*2, bar.height) : Math.max(batteryText.width + parent.spacing*2, bar.height) + height: bar.height + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + visible: false + + property bool clicked: false + + Component.onCompleted: { + batteryAvailibleScript.running = true + } + + Rectangle { + anchors.fill: parent + color: batteryButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25) + radius: 6 + border.width: 2 + border.color: batteryButton.containsMouse ? Colors.tertiaryContainer_fg + : Colors.secondaryContainer_fg + + Text { + id: batteryText + visible : !clicked + anchors.centerIn: parent + text:( + BatteryState.percentage >= 80 ? "" : + BatteryState.percentage >= 60 ? "" : + BatteryState.percentage >= 40 ? "" : + BatteryState.percentage >= 20 ? "" : + "" + ) + font.pixelSize: 26 + font.family: "Nerd Font" + color: ( + BatteryState.charging ? "green" : + BatteryState.percentage < 5 ? "red" : + BatteryState.percentage < 10 ? "yellow" : + batteryButton.containsMouse ? Colors.tertiary_fg : + Colors.primary + ) + } + + Text { + id: batteryPercentageText + visible : clicked + anchors.centerIn: parent + text: ( + BatteryState.charging ? "󰂄" : + BatteryState.percentage >= 90 ? "󰁹 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 80 ? "󰂂 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 70 ? "󰂁 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 60 ? "󰂀 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 50 ? "󰁿 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 40 ? "󰁾 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 30 ? "󰁽 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 20 ? "󰁼 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 10 ? "󰁻 " + BatteryState.percentage + "%" : + BatteryState.percentage >= 5 ? "󰁺 " + BatteryState.percentage + "%" : + "󰂃 " + BatteryState.percentage + "%" + ) + font.pixelSize: BatteryState.charging ? "󰂄" : batteryText.font.pixelSize*0.75 + font.family: "Nerd Font" + color: ( + BatteryState.percentage < 5 ? "red" : + BatteryState.charging ? "green" : + BatteryState.percentage < 10 ? "yellow" : + batteryButton.containsMouse ? Colors.tertiary_fg : + Colors.primary + ) + } + } + + + Process { + id: batteryAvailibleScript + command: ["sh", "-c", "upower -e | grep -q '^/org/freedesktop/UPower/devices/battery' && echo '1' || echo '0'"] + stdout: StdioCollector { + onStreamFinished: { + const hasBattery = this.text.trim() === "1" + + batteryButton.visible = hasBattery + if (hasBattery) { + BatteryState.start() + } + + console.log("battery available:", hasBattery) + } + } + } + + onClicked: { + batteryButton.clicked = !batteryButton.clicked + } + +} \ No newline at end of file