feat: improve dash perf placeholder & battery tank

This commit is contained in:
2 * r + 2 * t 2026-04-26 14:06:37 +10:00
parent 8f472f4a05
commit 76b4a0b63c
3 changed files with 161 additions and 142 deletions

View file

@ -1,10 +1,8 @@
import "performance"
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Services.UPower
import Caelestia.Config
import Caelestia.Internal
import Caelestia.Services
import qs.components
import qs.services
@ -12,27 +10,17 @@ import qs.services
Item {
id: root
readonly property int minWidth: 400 + 400 + Tokens.spacing.medium + 120 + Tokens.padding.extraLargeIncreased
implicitWidth: placeholder.active ? Tokens.sizes.dashboard.perfPlaceholderWidth : content.implicitWidth
implicitHeight: placeholder.active ? placeholder.implicitHeight + Tokens.padding.extraLarge * 2 : content.implicitHeight
function displayTemp(temp: real): string {
return `${Math.ceil(GlobalConfig.services.useFahrenheitPerformance ? temp * 1.8 + 32 : temp)}°${GlobalConfig.services.useFahrenheitPerformance ? "F" : "C"}`;
}
implicitWidth: Math.max(minWidth, content.implicitWidth)
implicitHeight: placeholder.visible ? placeholder.height : content.implicitHeight
StyledRect {
Loader {
id: placeholder
anchors.centerIn: parent
width: 400
height: 350
radius: Tokens.rounding.extraLarge
color: Colours.tPalette.m3surfaceContainer
visible: !Config.dashboard.performance.showCpu && !(Config.dashboard.performance.showGpu && Gpu.type !== Gpu.None) && !Config.dashboard.performance.showMemory && !Config.dashboard.performance.showStorage && !Config.dashboard.performance.showNetwork && !(UPower.displayDevice.isLaptopBattery && Config.dashboard.performance.showBattery)
active: !Config.dashboard.performance.showCpu && !(Config.dashboard.performance.showGpu && Gpu.type !== Gpu.None) && !Config.dashboard.performance.showMemory && !Config.dashboard.performance.showStorage && !Config.dashboard.performance.showNetwork && !(UPower.displayDevice.isLaptopBattery && Config.dashboard.performance.showBattery)
asynchronous: true
ColumnLayout {
anchors.centerIn: parent
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.medium
MaterialIcon {
@ -43,15 +31,16 @@ Item {
}
StyledText {
Layout.topMargin: -Tokens.spacing.small
Layout.alignment: Qt.AlignHCenter
text: qsTr("No widgets enabled")
font: Tokens.font.body.large
font: Tokens.font.title.large
color: Colours.palette.m3onSurface
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Enable widgets in dashboard settings")
text: qsTr("Enable widgets in the dashboard settings")
font: Tokens.font.body.small
color: Colours.palette.m3onSurfaceVariant
}
@ -64,7 +53,7 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
spacing: Tokens.spacing.medium
visible: !placeholder.visible
visible: !placeholder.active
ServiceRef {
service: root.Config.dashboard.performance.showCpu ? Cpu : null
@ -132,127 +121,8 @@ Item {
}
BatteryTank {
Layout.preferredWidth: 120
Layout.preferredHeight: mainColumn.implicitHeight
Layout.fillHeight: true
visible: UPower.displayDevice.isLaptopBattery && Config.dashboard.performance.showBattery
}
}
component BatteryTank: StyledClippingRect {
id: batteryTank
property real percentage: UPower.displayDevice.percentage
property bool isCharging: UPower.displayDevice.state === UPowerDeviceState.Charging
property color accentColor: Colours.palette.m3primary
property real animatedPercentage: 0
color: Colours.tPalette.m3surfaceContainer
radius: Tokens.rounding.extraLarge
Component.onCompleted: animatedPercentage = percentage
onPercentageChanged: animatedPercentage = percentage
// Background Fill
StyledRect {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: parent.height * batteryTank.animatedPercentage
color: Qt.alpha(batteryTank.accentColor, 0.15)
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
// Header Section
ColumnLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.small
MaterialIcon {
text: {
if (!UPower.displayDevice.isLaptopBattery) {
if (PowerProfiles.profile === PowerProfile.PowerSaver)
return "energy_savings_leaf";
if (PowerProfiles.profile === PowerProfile.Performance)
return "rocket_launch";
return "balance";
}
if (UPower.displayDevice.state === UPowerDeviceState.FullyCharged)
return "battery_full";
const perc = UPower.displayDevice.percentage;
const charging = [UPowerDeviceState.Charging, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state);
if (perc >= 0.99)
return "battery_full";
let level = Math.floor(perc * 7);
if (charging && (level === 4 || level === 1))
level--;
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
}
fontStyle: Tokens.font.icon.large
color: batteryTank.accentColor
}
StyledText {
Layout.fillWidth: true
text: qsTr("Battery")
font: Tokens.font.body.medium
color: Colours.palette.m3onSurface
}
}
Item {
Layout.fillHeight: true
}
// Bottom Info Section
ColumnLayout {
Layout.fillWidth: true
spacing: -4
StyledText {
Layout.alignment: Qt.AlignRight
text: `${Math.round(batteryTank.percentage * 100)}%`
font: Tokens.font.body.builders.large.size(28).weight(Font.Medium).build()
color: batteryTank.accentColor
}
StyledText {
Layout.alignment: Qt.AlignRight
text: {
if (UPower.displayDevice.state === UPowerDeviceState.FullyCharged)
return qsTr("Full");
if (batteryTank.isCharging)
return qsTr("Charging");
const s = UPower.displayDevice.timeToEmpty;
if (s === 0)
return qsTr("...");
const hr = Math.floor(s / 3600);
const min = Math.floor((s % 3600) / 60);
if (hr > 0)
return `${hr}h ${min}m`;
return `${min}m`;
}
font: Tokens.font.body.small
color: Colours.palette.m3onSurfaceVariant
}
}
}
Behavior on animatedPercentage {
Anim {
type: Anim.StandardLarge
}
}
}
}

View file

@ -0,0 +1,145 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Services.UPower
import Caelestia.Config
import Caelestia.Services
import qs.components
import qs.services
StyledClippingRect {
id: root
color: Colours.palette.m3secondaryContainer
radius: Tokens.rounding.large
implicitWidth: Config.dashboard.performance.showCpu || (Config.dashboard.performance.showGpu && Gpu.type !== Gpu.None) || Config.dashboard.performance.showStorage || Config.dashboard.performance.showMemory ? Tokens.sizes.dashboard.perfBattWidth : Tokens.sizes.dashboard.perfBattWidthSingle
implicitHeight: Tokens.sizes.dashboard.perfBattHeight
Contents {
id: layout
anchors.fill: parent
anchors.margins: Tokens.padding.medium
accentColour: Colours.palette.m3primary
textColour: Colours.palette.m3onSurface
subTextColour: Colours.palette.m3onSurfaceVariant
}
StyledRect {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: parent.height * UPower.displayDevice.percentage
color: Colours.palette.m3secondary
radius: Tokens.rounding.extraSmall
clip: true
Behavior on implicitHeight {
Anim {}
}
Contents {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: layout.anchors.margins
height: layout.height
accentColour: Colours.palette.m3primaryContainer
textColour: Colours.palette.m3onSecondary
subTextColour: Colours.palette.m3secondaryContainer
}
}
component Contents: ColumnLayout {
id: contents
required property color accentColour
required property color textColour
required property color subTextColour
readonly property bool charging: [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state)
spacing: 0
MaterialIcon {
Layout.leftMargin: -Tokens.padding.extraSmall
text: "battery_full"
color: contents.accentColour
fontStyle: Tokens.font.icon.large
}
StyledText {
Layout.fillWidth: true
text: qsTr("Battery")
color: contents.textColour
font: Tokens.font.body.medium
}
Item {
Layout.fillHeight: true
}
StyledText {
Layout.alignment: Qt.AlignRight
text: {
if (UPower.displayDevice.state === UPowerDeviceState.FullyCharged)
return qsTr("Full");
if (contents.charging)
return qsTr("Charging");
const s = UPower.displayDevice.timeToEmpty;
if (s === 0)
return qsTr("...");
const hr = Math.floor(s / 3600);
const min = Math.floor((s % 3600) / 60);
if (hr > 0)
return `${hr}h ${min}m`;
return `${min}m`;
}
color: contents.subTextColour
font: Tokens.font.body.small
animate: true
}
RowLayout {
Layout.topMargin: -Tokens.padding.extraSmall
Layout.bottomMargin: -Tokens.padding.small
Layout.rightMargin: -Tokens.padding.extraSmall
Layout.alignment: Qt.AlignRight
spacing: Tokens.spacing.extraSmall
MaterialIcon {
text: "bolt"
color: contents.accentColour
fontStyle: Tokens.font.icon.large
fill: 1
scale: contents.charging ? 1 : 0
opacity: contents.charging ? 1 : 0
Behavior on scale {
Anim {
type: Anim.FastSpatial
}
}
Behavior on opacity {
Anim {
type: Anim.FastEffects
}
}
}
StyledText {
text: `${Math.round(UPower.displayDevice.percentage * 100)}%`
color: contents.accentColour
font: Tokens.font.headline.medium
}
}
}
}

View file

@ -192,8 +192,12 @@ class DashboardTokens : public ConfigObject {
CONFIG_PROPERTY(int, perfHeroCardWidth, 400)
CONFIG_PROPERTY(int, perfUsageShapeSize, 100)
CONFIG_PROPERTY(int, perfStorageTextWidth, 160)
CONFIG_PROPERTY(int, perfNetworkCardWidth, 280)
CONFIG_PROPERTY(int, perfNetworkCardWidth, 390)
CONFIG_PROPERTY(int, perfNetworkCardHeight, 220)
CONFIG_PROPERTY(int, perfBattWidth, 150)
CONFIG_PROPERTY(int, perfBattWidthSingle, 400)
CONFIG_PROPERTY(int, perfBattHeight, 160)
CONFIG_PROPERTY(int, perfPlaceholderWidth, 700)
public:
explicit DashboardTokens(QObject* parent = nullptr)