From 2ffaf4eb3cce40584568f8f01b70f5d092d5dd93 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Wed, 4 Jun 2025 17:01:36 +1000 Subject: feat: bar battery popout --- modules/bar/Content.qml | 6 ++ modules/bar/components/StatusIcons.qml | 1 + modules/bar/popouts/Battery.qml | 176 +++++++++++++++++++++++++++++++++ modules/bar/popouts/Content.qml | 45 +++++---- 4 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 modules/bar/popouts/Battery.qml (limited to 'modules/bar') diff --git a/modules/bar/Content.qml b/modules/bar/Content.qml index f469a3f..1032050 100644 --- a/modules/bar/Content.qml +++ b/modules/bar/Content.qml @@ -14,10 +14,16 @@ StyledRect { function checkPopout(y: real): var { const aw = activeWindow.child; const awy = activeWindow.y + aw.y; + const b = statusIconsInner.battery; + const by = statusIcons.y + statusIconsInner.y + b.y; if (y >= awy && y <= awy + aw.implicitHeight) { Popouts.currentName = "activewindow"; Popouts.currentCenter = Qt.binding(() => activeWindow.y + aw.y + aw.implicitHeight / 2); Popouts.hasCurrent = true; + } else if (y >= by && y <= by + b.implicitHeight) { + Popouts.currentName = "battery"; + Popouts.currentCenter = Qt.binding(() => statusIcons.y + statusIconsInner.y + b.y + b.implicitHeight / 2); + Popouts.hasCurrent = true; } else { Popouts.hasCurrent = false; } diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml index abab10c..c1589a2 100644 --- a/modules/bar/components/StatusIcons.qml +++ b/modules/bar/components/StatusIcons.qml @@ -10,6 +10,7 @@ Item { id: root property color colour: Colours.palette.m3secondary + readonly property Item battery: battery clip: true implicitWidth: Math.max(network.implicitWidth, bluetooth.implicitWidth, devices.implicitWidth, battery.implicitWidth) diff --git a/modules/bar/popouts/Battery.qml b/modules/bar/popouts/Battery.qml new file mode 100644 index 0000000..e886649 --- /dev/null +++ b/modules/bar/popouts/Battery.qml @@ -0,0 +1,176 @@ +pragma ComponentBehavior: Bound + +import "root:/widgets" +import "root:/services" +import "root:/config" +import Quickshell.Services.UPower +import QtQuick + +Column { + id: root + + spacing: Appearance.spacing.normal + + StyledText { + text: `Remaining: ${Math.round(UPower.displayDevice.percentage * 100)}%` + } + + StyledText { + + function formatSeconds(s: int, fallback: string): string { + const day = Math.floor(s / 86400); + const hr = Math.floor(s / 3600) % 60; + const min = Math.floor(s / 60) % 60; + + let comps = []; + if (day > 0) + comps.push(`${day} days`); + if (hr > 0) + comps.push(`${hr} hours`); + if (min > 0) + comps.push(`${min} mins`); + + return comps.join(", ") || fallback; + } + + text: `Time ${UPower.onBattery ? "remaining" : "until charged"}: ${UPower.onBattery ? formatSeconds(UPower.displayDevice.timeToEmpty, "Calculating...") : formatSeconds(UPower.displayDevice.timeToFull, "Fully charged!")}` + } + + StyledRect { + id: profiles + + property string current: { + const p = PowerProfiles.profile; + if (p === PowerProfile.PowerSaver) + return saver.icon; + if (p === PowerProfile.Performance) + return perf.icon; + return balance.icon; + } + + anchors.horizontalCenter: parent.horizontalCenter + + implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + Appearance.padding.normal * 2 + Appearance.spacing.large * 2 + implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + Appearance.padding.small * 2 + + color: Colours.palette.m3surfaceContainer + radius: Appearance.rounding.full + + StyledRect { + id: indicator + + color: Colours.palette.m3primary + radius: Appearance.rounding.full + state: profiles.current + + states: [ + State { + name: saver.icon + + Fill { + item: saver + } + }, + State { + name: balance.icon + + Fill { + item: balance + } + }, + State { + name: perf.icon + + Fill { + item: perf + } + } + ] + + transitions: Transition { + AnchorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.emphasized + } + } + } + + Profile { + id: saver + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Appearance.padding.small + + profile: PowerProfile.PowerSaver + icon: "energy_savings_leaf" + } + + Profile { + id: balance + + anchors.centerIn: parent + + profile: PowerProfile.Balanced + icon: "balance" + } + + Profile { + id: perf + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Appearance.padding.small + + profile: PowerProfile.Performance + icon: "rocket_launch" + } + } + + component Fill: AnchorChanges { + required property Item item + + target: indicator + anchors.left: item.left + anchors.right: item.right + anchors.top: item.top + anchors.bottom: item.bottom + } + + component Profile: Item { + required property string icon + required property int profile + + implicitWidth: icon.implicitHeight + Appearance.padding.small * 2 + implicitHeight: icon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + radius: Appearance.rounding.full + color: profiles.current === parent.icon ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + + function onClicked(): void { + PowerProfiles.profile = parent.profile; + } + } + + MaterialIcon { + id: icon + + anchors.centerIn: parent + + text: parent.icon + font.pointSize: Appearance.font.size.large + color: profiles.current === text ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + fill: profiles.current === text ? 1 : 0 + + Behavior on fill { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + } +} diff --git a/modules/bar/popouts/Content.qml b/modules/bar/popouts/Content.qml index c10348c..6b65f56 100644 --- a/modules/bar/popouts/Content.qml +++ b/modules/bar/popouts/Content.qml @@ -23,7 +23,12 @@ Item { Popout { name: "activewindow" - sourceComponent: ActiveWindow {} + source: "ActiveWindow.qml" + } + + Popout { + name: "battery" + source: "Battery.qml" } } @@ -51,25 +56,25 @@ Item { active: shouldBeActive asynchronous: true - Behavior on active { - SequentialAnimation { - Anim { - target: popout - property: "opacity" - from: popout.shouldBeActive ? 1 : 0 - to: popout.shouldBeActive ? 0 : 1 - duration: popout.shouldBeActive ? 0 : Appearance.anim.durations.normal - } - PropertyAction {} - Anim { - target: popout - property: "opacity" - from: popout.shouldBeActive ? 0 : 1 - to: popout.shouldBeActive ? 1 : 0 - duration: popout.shouldBeActive ? Appearance.anim.durations.normal : 0 - } - } - } + // Behavior on active { + // SequentialAnimation { + // Anim { + // target: popout + // property: "opacity" + // from: popout.shouldBeActive ? 1 : 0 + // to: popout.shouldBeActive ? 0 : 1 + // duration: popout.shouldBeActive ? 0 : Appearance.anim.durations.normal + // } + // PropertyAction {} + // Anim { + // target: popout + // property: "opacity" + // from: popout.shouldBeActive ? 0 : 1 + // to: popout.shouldBeActive ? 1 : 0 + // duration: popout.shouldBeActive ? Appearance.anim.durations.normal : 0 + // } + // } + // } } component Anim: NumberAnimation { -- cgit v1.2.3-freya