Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 117 additions & 88 deletions quickshell/Modules/Settings/BatteryTab.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import qs.Modules.Settings.Widgets
Item {
id: root

readonly property color batteryStatusColor: {
if (BatteryService.isLowBattery && !BatteryService.isCharging)
return Theme.error;
if (BatteryService.isCharging || BatteryService.isPluggedIn)
return Theme.primary;
return Theme.surfaceText;
}

Process {
id: applyLimitProcess
command: ["pkexec", "sh", "-c", "
Expand Down Expand Up @@ -47,8 +55,6 @@ done
// 1. Information Card
SettingsCard {
width: parent.width
iconName: "battery_charging_full"
title: I18n.tr("Status")
settingKey: "batteryStatusCard"
tags: ["battery", "status", "charge", "health"]

Expand All @@ -57,108 +63,131 @@ done
x: Theme.spacingM
spacing: Theme.spacingM

SettingsDivider {}

Row {
width: parent.width
StyledText {
text: I18n.tr("Power source")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: BatteryService.isPluggedIn ? I18n.tr("AC Adapter (Plugged In)") : I18n.tr("Battery Power")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
}
}
spacing: Theme.spacingM

SettingsDivider {}

Row {
width: parent.width
StyledText {
text: I18n.tr("Charge Level")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
DankIcon {
name: BatteryService.getBatteryIcon()
size: Theme.iconSizeLarge
color: root.batteryStatusColor
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: `${BatteryService.batteryLevel}%`
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
}
}

SettingsDivider {}

Row {
width: parent.width
StyledText {
text: I18n.tr("Status")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: BatteryService.batteryStatus
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
Column {
spacing: Theme.spacingXS
width: parent.width - Theme.iconSizeLarge - Theme.spacingM
anchors.verticalCenter: parent.verticalCenter

Row {
spacing: Theme.spacingS
width: parent.width

StyledText {
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : I18n.tr("Power")
font.pixelSize: Theme.fontSizeXLarge
font.weight: Font.Bold
color: root.batteryStatusColor
}

StyledText {
text: BatteryService.batteryAvailable ? BatteryService.batteryStatus : I18n.tr("Management")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
elide: Text.ElideRight
width: Math.max(0, parent.width - 100)
}
}

StyledText {
text: BatteryService.isPluggedIn ? I18n.tr("Plugged In (AC)") : I18n.tr("Battery Power")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
width: parent.width
elide: Text.ElideRight
}
}
}

SettingsDivider {}

Row {
Item {
width: parent.width
StyledText {
text: I18n.tr("Estimated Time")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
height: 4

Rectangle {
anchors.fill: parent
radius: height / 2
color: Theme.withAlpha(Theme.primary, 0.16)
}
StyledText {
text: BatteryService.formatTimeRemaining()
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft

Rectangle {
width: parent.width * Math.max(0, Math.min(1, BatteryService.batteryLevel / 100))
height: parent.height
radius: height / 2
color: root.batteryStatusColor
visible: BatteryService.batteryAvailable
}
}

SettingsDivider {}

Row {
width: parent.width
StyledText {
text: I18n.tr("Battery Health")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft
spacing: Theme.spacingM

Item {
width: (parent.width - Theme.spacingM) / 2
height: timeColumn.implicitHeight

Column {
id: timeColumn
width: parent.width
spacing: Theme.spacingXXS

StyledText {
text: I18n.tr("Estimated time")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}

StyledText {
text: {
const remaining = BatteryService.formatTimeRemaining();
const estimated = BatteryService.formatEstimatedTime();
return estimated ? `${remaining} (${estimated})` : remaining;
}
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
}
}
StyledText {
text: BatteryService.batteryHealth
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width / 2
horizontalAlignment: Text.AlignLeft

Item {
width: (parent.width - Theme.spacingM) / 2
height: healthColumn.implicitHeight

Column {
id: healthColumn
width: parent.width
spacing: Theme.spacingXXS

StyledText {
text: I18n.tr("Battery health")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}

StyledText {
text: BatteryService.batteryHealth
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
}
}
}
}
Expand Down
31 changes: 24 additions & 7 deletions quickshell/Services/BatteryService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -405,23 +405,40 @@ Singleton {
return btDevices;
}

// Format time remaining for charge/discharge
function formatTimeRemaining() {
function estimatedSeconds() {
if (!batteryAvailable) {
return "Unknown";
return 0;
}

const rate = _smoothedChangeRate > 0 ? _smoothedChangeRate : changeRate;
const totalTime = (isCharging) ? ((batteryCapacity - batteryEnergy) / rate) : (batteryEnergy / rate);
const avgTime = Math.abs(totalTime * 3600);
if (!avgTime || avgTime <= 0 || avgTime > 86400)
const seconds = Math.abs(totalTime * 3600);
if (!seconds || seconds <= 0 || seconds > 86400)
return 0;
return seconds;
}

// Format time remaining for charge/discharge
function formatTimeRemaining() {
const seconds = estimatedSeconds();
if (!seconds)
return "Unknown";

const hours = Math.floor(avgTime / 3600);
const minutes = Math.floor((avgTime % 3600) / 60);
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
}

function formatEstimatedTime() {
const seconds = estimatedSeconds();
if (!seconds)
return "";

const target = new Date(Date.now() + seconds * 1000);
const use24Hour = SettingsData.use24HourClock !== false;
return target.toLocaleTimeString(Qt.locale(), use24Hour ? "HH:mm" : "h:mm AP");
}

function getBatteryIcon() {
if (!batteryAvailable) {
return "power";
Expand Down
35 changes: 20 additions & 15 deletions quickshell/translations/settings_search_index.json
Original file line number Diff line number Diff line change
Expand Up @@ -11141,6 +11141,26 @@
],
"description": "Show a notification when battery reaches the charge limit."
},
{
"section": "batteryStatusCard",
"label": "Power",
"tabIndex": 42,
"category": "Power & Security",
"keywords": [
"battery",
"charge",
"health",
"hibernate",
"power",
"reboot",
"restart",
"security",
"shutdown",
"sleep",
"status",
"suspend"
]
},
{
"section": "_tab_42",
"label": "Power & Security",
Expand Down Expand Up @@ -11233,21 +11253,6 @@
"icon": "tune",
"description": "Limit the maximum battery charge level to extend lifespan."
},
{
"section": "batteryStatusCard",
"label": "Status",
"tabIndex": 42,
"category": "Power & Security",
"keywords": [
"battery",
"charge",
"health",
"power",
"security",
"status"
],
"icon": "battery_charging_full"
},
{
"section": "_tab_43",
"label": "Widgets & Notifications",
Expand Down
Loading