refactoring 3/?

This commit is contained in:
Turann_
2026-01-28 00:57:26 +03:00
parent a0c0983a62
commit 117ffe5550
5 changed files with 101 additions and 94 deletions
+4 -11
View File
@@ -58,23 +58,16 @@ Item {
readonly property var bluetoothDevice: device && BatteryService.isBluetoothDevice(device) ? device : null
readonly property bool hasBluetoothBattery: BatteryService.isBluetoothDevice(device)
readonly property bool isReady: testMode ? true : (initializationComplete && BatteryService.isDeviceReady(device))
readonly property bool isReady: testMode ? true : (BatteryService.ready && BatteryService.isDeviceReady(device))
readonly property real percent: testMode ? testPercent : (isReady ? BatteryService.getPercentage(device) : 0)
readonly property bool isCharging: testMode ? testCharging : (isReady ? BatteryService.isCharging(device) : false)
readonly property bool isPluggedIn: testMode ? testPluggedIn : (isReady ? BatteryService.isPluggedIn(device) : false)
property bool initializationComplete: false
property bool hasNotifiedLowBattery: false
visible: shouldShow
opacity: shouldShow ? 1.0 : 0.0
Timer {
interval: 500
running: true
onTriggered: root.initializationComplete = true
}
readonly property bool isDevicePresent: {
if (testMode)
return true;
@@ -156,9 +149,9 @@ Item {
suffix: "%"
autoHide: false
forceOpen: isReady && displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide" || (initializationComplete && !isReady)
customBackgroundColor: !initializationComplete ? "transparent" : (isCharging ? Color.mPrimary : (isLowBattery ? Color.mError : "transparent"))
customTextIconColor: !initializationComplete ? "transparent" : (isCharging ? Color.mOnPrimary : (isLowBattery ? Color.mOnError : "transparent"))
forceClose: displayMode === "alwaysHide" || (BatteryService.ready && !isReady)
customBackgroundColor: !BatteryService.ready ? "transparent" : (isCharging ? Color.mPrimary : (isLowBattery ? Color.mError : "transparent"))
customTextIconColor: !BatteryService.ready ? "transparent" : (isCharging ? Color.mOnPrimary : (isLowBattery ? Color.mOnError : "transparent"))
tooltipText: {
let lines = [];
+1 -7
View File
@@ -77,14 +77,8 @@ Loader {
Item {
id: batteryIndicator
property bool initializationComplete: false
Timer {
interval: 500
running: true
onTriggered: batteryIndicator.initializationComplete = true
}
property bool isReady: initializationComplete && BatteryService.batteryReady
property bool isReady: BatteryService.ready && BatteryService.batteryReady
property real percent: BatteryService.batteryPercentage
property bool charging: BatteryService.batteryCharging
property bool pluggedIn: BatteryService.batteryPluggedIn
+3 -23
View File
@@ -58,30 +58,10 @@ SmartPanel {
// Use the centralized list of all devices
readonly property var allDevices: BatteryService.devices
readonly property var laptopBatteries: allDevices.filter(d => !BatteryService.isBluetoothDevice(d))
readonly property var otherDevices: allDevices.filter(d => BatteryService.isBluetoothDevice(d))
readonly property var laptopBatteries: BatteryService.laptopBatteries
readonly property var otherDevices: BatteryService.externalBatteries
readonly property string timeText: {
if (!isReady || !isDevicePresent) {
return I18n.tr("battery.no-battery-detected");
}
if (isPluggedIn) {
return I18n.tr("battery.plugged-in");
}
if (selectedDevice) {
if (selectedDevice.timeToFull > 0) {
return I18n.tr("battery.time-until-full", {
"time": Time.formatVagueHumanReadableDuration(selectedDevice.timeToFull)
});
}
if (selectedDevice.timeToEmpty > 0) {
return I18n.tr("battery.time-left", {
"time": Time.formatVagueHumanReadableDuration(selectedDevice.timeToEmpty)
});
}
}
return I18n.tr("common.idle");
}
readonly property string timeText: BatteryService.getTimeRemainingText(selectedDevice)
readonly property string iconName: BatteryService.getIcon(percent, isCharging, isPluggedIn, isReady)
property var batteryWidgetInstance: BarService.lookupWidget("Battery", screen ? screen.name : null)
@@ -1,8 +1,8 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Services.UPower
import qs.Commons
import qs.Services.Hardware
import qs.Widgets
ColumnLayout {
@@ -22,58 +22,12 @@ ColumnLayout {
property bool valueHideIfNotDetected: widgetData.hideIfNotDetected !== undefined ? widgetData.hideIfNotDetected : widgetMetadata.hideIfNotDetected
property bool valueHideIfIdle: widgetData.hideIfIdle !== undefined ? widgetData.hideIfIdle : widgetMetadata.hideIfIdle
// Build model of available battery devices
function buildDeviceModel() {
var model = [
{
"key": "",
"name": I18n.tr("bar.battery.device-default")
}
];
if (!UPower.devices) {
return model;
}
var deviceArray = UPower.devices.values || [];
for (var i = 0; i < deviceArray.length; i++) {
var device = deviceArray[i];
if (!device || device.type === UPowerDeviceType.LinePower) {
continue;
}
var displayName = device.model || device.nativePath || "Unknown";
model.push({
"key": device.nativePath || "",
"name": displayName
});
}
return model;
}
readonly property int _deviceCount: (UPower.devices && UPower.devices.values) ? UPower.devices.values.length : 0
property var deviceModel: buildDeviceModel()
on_DeviceCountChanged: {
deviceModel = buildDeviceModel();
}
property var deviceModel: BatteryService.getDeviceOptionsModel()
Connections {
target: UPower.devices
function onValuesChanged() {
deviceModel = buildDeviceModel();
}
}
Timer {
id: refreshTimer
interval: 2000
running: true
repeat: true
onTriggered: {
var currentCount = (UPower.devices && UPower.devices.values) ? UPower.devices.values.length : 0;
if (currentCount !== root._deviceCount) {
deviceModel = buildDeviceModel();
}
target: BatteryService
function onDevicesChanged() {
deviceModel = BatteryService.getDeviceOptionsModel();
}
}
@@ -123,7 +77,7 @@ ColumnLayout {
NIconButton {
icon: "refresh"
tooltipText: "Refresh device list"
onClicked: deviceModel = buildDeviceModel()
onClicked: deviceModel = BatteryService.getDeviceOptionsModel()
}
}
+87 -1
View File
@@ -59,6 +59,11 @@ Singleton {
if (devices.length === 0)
return null;
// Prioritize DisplayDevice (Aggregate)
if (UPower.displayDevice && UPower.displayDevice.type === UPowerDeviceType.Battery && isDevicePresent(UPower.displayDevice)) {
return UPower.displayDevice;
}
// Prioritize BAT0
for (var i = 0; i < devices.length; i++) {
var d = devices[i];
@@ -89,15 +94,35 @@ Singleton {
readonly property bool batteryReady: isDeviceReady(primaryDevice)
readonly property bool batteryPresent: isDevicePresent(primaryDevice)
// Exposed subsets of devices
readonly property var laptopBatteries: devices.filter(d => !isBluetoothDevice(d))
readonly property var externalBatteries: devices.filter(d => isBluetoothDevice(d))
property bool healthAvailable: false
property int healthPercent: -1
// Initialization state
property bool initializationComplete: false
readonly property bool ready: initializationComplete
Timer {
interval: 500
running: true
repeat: false
onTriggered: root.initializationComplete = true
}
// 3. Helper to resolve a device by path, or return primary if path is empty/invalid
function resolveDevice(nativePath) {
if (!nativePath || nativePath === "") {
return primaryDevice;
}
// Check for DisplayDevice explicitly if requested via "DisplayDevice" or empty string
if (nativePath === "DisplayDevice" && UPower.displayDevice) {
return UPower.displayDevice;
}
// Search in our cached list
for (var i = 0; i < devices.length; i++) {
var d = devices[i];
@@ -189,6 +214,65 @@ Singleton {
return "";
}
function getTimeRemainingText(device) {
if (!ready || !isDevicePresent(device)) {
return I18n.tr("battery.no-battery-detected");
}
if (isPluggedIn(device)) {
return I18n.tr("battery.plugged-in");
}
if (device) {
if (device.timeToFull > 0) {
return I18n.tr("battery.time-until-full", {
"time": Time.formatVagueHumanReadableDuration(device.timeToFull)
});
}
if (device.timeToEmpty > 0) {
return I18n.tr("battery.time-left", {
"time": Time.formatVagueHumanReadableDuration(device.timeToEmpty)
});
}
}
return I18n.tr("common.idle");
}
function getDeviceOptionsModel() {
var model = [{
"key": "",
"name": I18n.tr("bar.battery.device-default")
}];
for (var i = 0; i < devices.length; i++) {
var d = devices[i];
var name = "";
// Determine friendly name
if (isBluetoothDevice(d)) {
name = d.name || "Bluetooth Device";
} else if (d === UPower.displayDevice) {
name = I18n.tr("common.battery-aggregate") || "Display Device";
} else {
name = d.model || I18n.tr("common.battery");
}
// Determine ID/Path
var key = isBluetoothDevice(d) ? d.address : d.nativePath;
if (!key && d === UPower.displayDevice) key = "DisplayDevice";
// Format: "Model (ID)"
var displayName = name;
if (key && key !== "DisplayDevice") {
displayName = `${name} (${key})`;
}
model.push({
"key": key || "",
"name": displayName
});
}
return model;
}
function refreshHealth() {
if (!isLaptopBattery || !primaryDevice) {
healthAvailable = false;
@@ -200,7 +284,8 @@ Singleton {
Process {
id: healthProcess
command: ["sh", "-c", "upower -i $(upower -e | grep battery | head -n 1) 2>/dev/null | grep -iE 'capacity'"]
// Dynamically target the primary device if possible, otherwise fall back to first battery
command: ["sh", "-c", `upower -i ${primaryDevice.nativePath ? "/org/freedesktop/UPower/devices/battery_" + primaryDevice.nativePath : "$(upower -e | grep battery | head -n 1)"} 2>/dev/null | grep -iE 'capacity'`]
environment: ({
"LC_ALL": "C"
})
@@ -227,6 +312,7 @@ Singleton {
}
}
function getIcon(percent, charging, pluggedIn, isReady) {
if (!isReady) {
return "battery-exclamation";