Files
z-bar-qt/Modules/Bar/Popouts/UPowerPopout.qml
T
zach 7d6f3cc275
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 20s
JS/TS / lint (pull_request) Successful in 22s
Python / static (pull_request) Successful in 57s
Rust / fmt (pull_request) Successful in 1m2s
C++ / build (pull_request) Successful in 2m10s
Rust / build (pull_request) Successful in 1m50s
Rust / clippy (pull_request) Successful in 1m25s
Python / verify (pull_request) Successful in 2m58s
C++ / clang-tidy (pull_request) Successful in 7m8s
initial commit for media widget update + anim tokens restructure
2026-08-18 16:14:41 +02:00

260 lines
5.9 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.UPower
import ZShell.Config
import qs.Modules.Bar.Components.Common
import qs.Helpers
import qs.Components
import qs.Services
Column {
id: root
required property bool horizontal
readonly property int panelRadius: ((profiles.height / 2) + Tokens.padding.extraSmall) * Tokens.rounding.scale
spacing: Tokens.spacing.medium
Loader {
active: Battery.isLaptop
CustomText {
text: qsTr("Remaining: %1%").arg(Math.round(UPower.displayDevice.percentage * 100))
}
}
Item {
id: padding
implicitHeight: root.horizontal ? 0 : Tokens.padding.small
implicitWidth: 1
}
Repeater {
model: Battery.allPeripherals
RowLayout {
id: layout
required property var modelData
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
CustomText {
Layout.fillWidth: true
text: layout.modelData.model
}
BatteryIcon {
devState: layout.modelData.state
percentage: layout.modelData.percentage
}
}
}
CustomText {
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;
}
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
text: Battery.isLaptop ? qsTr("Time %1: %2").arg(Battery.onBattery ? "remaining" : "until charged").arg(Battery.onBattery ? formatSeconds(Battery.timeToEmpty, "Calculating...") : formatSeconds(Battery.timeToFull, "Fully charged!")) : qsTr("Power profile: %1").arg(PowerProfile.toString(PowerProfiles.profile))
}
Loader {
active: PowerProfiles.degradationReason !== PerformanceDegradationReason.None
anchors.horizontalCenter: parent.horizontalCenter
asynchronous: true
height: active ? ((item as Item)?.implicitHeight ?? 0) : 0
sourceComponent: CustomRect {
color: Colors.palette.m3error
implicitHeight: child.implicitHeight + Tokens.padding.large
implicitWidth: child.implicitWidth + Tokens.padding.small * 2
radius: Tokens.rounding.largeIncreased
Column {
id: child
anchors.centerIn: parent
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Tokens.spacing.small
MaterialIcon {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -font.pointSize / 10
color: Colors.palette.m3onError
text: "warning"
}
CustomText {
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onError
font.family: Config.appearance.font.family.mono
text: qsTr("Performance Degraded")
}
MaterialIcon {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -font.pointSize / 10
color: Colors.palette.m3onError
text: "warning"
}
}
CustomText {
anchors.horizontalCenter: parent.horizontalCenter
color: Colors.palette.m3onError
text: qsTr("Reason: %1").arg(PerformanceDegradationReason.toString(PowerProfiles.degradationReason))
}
}
}
}
CustomRect {
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
color: Colors.tPalette.m3surfaceContainer
implicitHeight: indicator.height + Tokens.padding.extraSmall * 2
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + Tokens.padding.small * 2 + Tokens.spacing.small * 8
radius: Tokens.rounding.full
CustomRect {
id: indicator
color: Colors.palette.m3primary
radius: Tokens.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 {
AnchorAnim {
}
}
}
Profile {
id: saver
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
icon: "energy_savings_leaf"
profile: PowerProfile.PowerSaver
}
Profile {
id: balance
anchors.centerIn: parent
icon: "balance"
profile: PowerProfile.Balanced
}
Profile {
id: perf
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
icon: "bolt"
profile: PowerProfile.Performance
}
}
component Fill: AnchorChanges {
required property Item item
anchors.bottom: item.bottom
anchors.left: item.left
anchors.right: item.right
anchors.top: item.top
target: indicator
}
component Profile: Item {
required property string icon
required property int profile
implicitHeight: icon.implicitHeight + Tokens.padding.extraSmall
implicitWidth: icon.implicitHeight + Tokens.padding.extraSmall
StateLayer {
color: profiles.current === parent.icon ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
radius: Tokens.rounding.full
onClicked: {
PowerProfiles.profile = parent.profile;
}
}
MaterialIcon {
id: icon
anchors.centerIn: parent
color: profiles.current === text ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
fill: profiles.current === text ? 1 : 0
font.pointSize: Tokens.font.size.large * 2
grade: Colors.light ? 0 : (text === "balance" ? -25 : 0)
text: parent.icon
Behavior on fill {
Anim {
type: Anim.DefaultEffects
}
}
}
}
}