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
260 lines
5.9 KiB
QML
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|