battery-widget: better tooltip, reworked logic and improved display

This commit is contained in:
Lemmy
2026-02-05 22:41:04 -05:00
parent effc04b588
commit c2237baab3
23 changed files with 67 additions and 30 deletions
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Akkugerät",
"display-mode-description": "Wählen Sie, wie der Akku in der Leiste angezeigt wird.",
"display-mode-graphic": "Grafischer Akku",
"display-mode-graphic-clean": "Grafische Batterie (ohne %)",
"display-mode-icon-always": "Symbol - Immer % anzeigen",
"display-mode-icon-hover": "Symbol - Beim Hover anzeigen",
"display-mode-icon-only": "Nur Symbol",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Battery device",
"display-mode-description": "Choose how the battery is displayed in the bar.",
"display-mode-graphic": "Graphical battery",
"display-mode-graphic-clean": "Graphical battery (no %)",
"display-mode-icon-always": "Icon - Always show %",
"display-mode-icon-hover": "Icon - Show on hover",
"display-mode-icon-only": "Icon only",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Dispositivo de batería",
"display-mode-description": "Elige cómo se muestra la batería en la barra.",
"display-mode-graphic": "Batería gráfica",
"display-mode-graphic-clean": "Batería gráfica (sin %)",
"display-mode-icon-always": "Icono - Siempre mostrar %",
"display-mode-icon-hover": "Icono - Mostrar al pasar",
"display-mode-icon-only": "Solo icono",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Dispositif à pile",
"display-mode-description": "Choisissez comment la batterie est affichée dans la barre.",
"display-mode-graphic": "Batterie graphique",
"display-mode-graphic-clean": "Batterie graphique (sans %)",
"display-mode-icon-always": "Icône - Toujours afficher %",
"display-mode-icon-hover": "Icône - Afficher au survol",
"display-mode-icon-only": "Icône seulement",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Akkumulátor eszköz",
"display-mode-description": "Válassza ki, hogyan jelenjen meg az akkumulátor a sávban.",
"display-mode-graphic": "Grafikus akkumulátor",
"display-mode-graphic-clean": "Grafikus akkumulátor (százalék nélkül)",
"display-mode-icon-always": "Ikon - Mindig mutassa a %-ot",
"display-mode-icon-hover": "Ikon - Rámutatásra megjelenik",
"display-mode-icon-only": "Csak ikon",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "バッテリーデバイス",
"display-mode-description": "バッテリーをバーにどのように表示するかを選択します。",
"display-mode-graphic": "グラフィックバッテリー",
"display-mode-graphic-clean": "グラフィカルバッテリー (パーセント表示なし)",
"display-mode-icon-always": "アイコン - 常に%を表示",
"display-mode-icon-hover": "アイコン - ホバー時に表示",
"display-mode-icon-only": "アイコンのみ",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "배터리 장치",
"display-mode-description": "배터리가 바에 어떻게 표시될지 선택하세요.",
"display-mode-graphic": "그래픽 배터리",
"display-mode-graphic-clean": "그래픽 배터리 (퍼센트 없음)",
"display-mode-icon-always": "아이콘 - 항상 % 표시",
"display-mode-icon-hover": "아이콘 - 호버 시 표시",
"display-mode-icon-only": "아이콘만",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Accuapparaat",
"display-mode-description": "Kies hoe de batterij wordt weergegeven in de balk.",
"display-mode-graphic": "Grafische accu",
"display-mode-graphic-clean": "Grafische batterij (geen %)",
"display-mode-icon-always": "Pictogram - Altijd % tonen",
"display-mode-icon-hover": "Icoon - Tonen bij hover",
"display-mode-icon-only": "Alleen pictogram",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Urządzenie baterii",
"display-mode-description": "Wybierz, jak bateria jest wyświetlana na pasku.",
"display-mode-graphic": "Graficzna bateria",
"display-mode-graphic-clean": "Graficzna bateria (bez %)",
"display-mode-icon-always": "Ikona - Zawsze pokazuj %",
"display-mode-icon-hover": "Ikona - Pokaż po najechaniu",
"display-mode-icon-only": "Tylko ikona",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Dispositivo de bateria",
"display-mode-description": "Escolha como a bateria é exibida na barra.",
"display-mode-graphic": "Bateria gráfica",
"display-mode-graphic-clean": "Bateria gráfica (sem %)",
"display-mode-icon-always": "Ícone - Sempre mostrar %",
"display-mode-icon-hover": "Ícone - Mostrar ao passar",
"display-mode-icon-only": "Apenas ícone",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Устройство с батареей",
"display-mode-description": "Выберите, как отображается батарея на панели.",
"display-mode-graphic": "Графическая батарея",
"display-mode-graphic-clean": "Графическая батарея (без %)",
"display-mode-icon-always": "Значок - Всегда показывать %",
"display-mode-icon-hover": "Иконка - Показать при наведении",
"display-mode-icon-only": "Только значок",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Batterienhet",
"display-mode-description": "Välj hur batteriet visas i fältet.",
"display-mode-graphic": "Grafiskt batteri",
"display-mode-graphic-clean": "Grafiskt batteri (ingen %)",
"display-mode-icon-always": "Ikon - Visa alltid %",
"display-mode-icon-hover": "Ikon - Visa vid hovring",
"display-mode-icon-only": "Endast ikon",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Pil cihazı",
"display-mode-description": "Pilin çubukta nasıl görüntüleneceğini seçin.",
"display-mode-graphic": "Grafik pil",
"display-mode-graphic-clean": "Grafik pil (yüzde yok)",
"display-mode-icon-always": "Simge - Her zaman % göster",
"display-mode-icon-hover": "Simge - Üzerine gelindiğinde göster",
"display-mode-icon-only": "Yalnızca simge",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "Пристрій живлення від батареї",
"display-mode-description": "Виберіть, як відображається батарея на панелі.",
"display-mode-graphic": "Графічна батарея",
"display-mode-graphic-clean": "Графічна батарея (без %)",
"display-mode-icon-always": "Іконка - Завжди показувати %",
"display-mode-icon-hover": "Іконка - Показувати при наведенні",
"display-mode-icon-only": "Лише значок",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "电池设备",
"display-mode-description": "选择电池在任务栏中的显示方式。",
"display-mode-graphic": "图形化电池",
"display-mode-graphic-clean": "图形电池(无%",
"display-mode-icon-always": "图标 - 始终显示 %",
"display-mode-icon-hover": "图标 - 悬停时显示",
"display-mode-icon-only": "仅图标",
+1
View File
@@ -50,6 +50,7 @@
"device-label": "電池裝置",
"display-mode-description": "選擇電池在工具列中的顯示方式。",
"display-mode-graphic": "圖形化電池",
"display-mode-graphic-clean": "圖形電池(無%",
"display-mode-icon-always": "圖示 - 始終顯示 %",
"display-mode-icon-hover": "圖示 - 懸停時顯示",
"display-mode-icon-only": "僅圖示",
+1 -1
View File
@@ -12,7 +12,7 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property var tooltipText: ""
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
+1 -1
View File
@@ -13,7 +13,7 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property var tooltipText: ""
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
+1 -1
View File
@@ -13,7 +13,7 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property var tooltipText: ""
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
+35 -17
View File
@@ -39,7 +39,7 @@ Item {
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
readonly property bool useGraphicMode: displayMode === "graphic"
readonly property bool useGraphicMode: displayMode === "graphic" || displayMode === "graphic-clean"
readonly property bool hideIfNotDetected: widgetSettings.hideIfNotDetected !== undefined ? widgetSettings.hideIfNotDetected : widgetMetadata.hideIfNotDetected
readonly property bool hideIfIdle: widgetSettings.hideIfIdle !== undefined ? widgetSettings.hideIfIdle : widgetMetadata.hideIfIdle
@@ -58,53 +58,58 @@ Item {
readonly property string deviceNativePath: widgetSettings.deviceNativePath !== undefined ? widgetSettings.deviceNativePath : widgetMetadata.deviceNativePath
readonly property var selectedDevice: BatteryService.isDevicePresent(BatteryService.findDevice(deviceNativePath)) ? BatteryService.findDevice(deviceNativePath) : null
readonly property string tooltipText: {
let lines = [];
readonly property var tooltipContent: {
if (!isReady || !isPresent) {
return I18n.tr("battery.no-battery-detected");
}
const isInternal = selectedDevice.isLaptopBattery;
let rows = [];
const isInternal = selectedDevice.isLaptopBattery;
if (isInternal) {
// Show charge percentage
lines.push(`${I18n.tr("battery.battery-level")}: ${percent}%`);
rows.push([I18n.tr("battery.battery-level"), `${percent}%`]);
let timeText = BatteryService.getTimeRemainingText(selectedDevice);
if (timeText) {
lines.push(timeText);
const entries = timeText.split(":").map(a => a.trim());
rows.push(entries);
}
let rateText = BatteryService.getRateText(selectedDevice);
console.log(rateText);
if (rateText) {
lines.push(rateText);
const entries = rateText.split(":").map(a => a.trim());
rows.push(entries);
}
// Show battery health if supported (check actual battery, not DisplayDevice)
let healthDevice = selectedDevice.healthSupported ? selectedDevice : (BatteryService.laptopBatteries.length > 0 ? BatteryService.laptopBatteries[0] : null);
if (healthDevice && healthDevice.healthSupported) {
lines.push(`${I18n.tr("battery.battery-health")}: ${Math.round(healthDevice.healthPercentage)}%`);
rows.push([I18n.tr("battery.battery-health"), `${Math.round(healthDevice.healthPercentage)}%`]);
}
} else if (selectedDevice) {
// External / Peripheral Device (Phone, Keyboard, Mouse, Gamepad, Headphone etc.)
let name = BatteryService.getDeviceName(selectedDevice);
lines.push(`${name}: ${percent}%`);
rows.push([name, `${percent}%`]);
}
// If we are showing the main laptop battery, append external devices
if (isInternal) {
var external = BatteryService.bluetoothBatteries;
if (external.length > 0) {
if (lines.length > 0)
lines.push(""); // Separator
if (rows.length > 0) {
rows.push(["---", "---"]); // Separator
}
for (var j = 0; j < external.length; j++) {
var dev = external[j];
var dName = BatteryService.getDeviceName(dev);
var dPct = BatteryService.getPercentage(dev);
lines.push(`${dName}: ${dPct}%`);
rows.push([dName, `${dPct}%`]);
}
}
}
return lines.join("\n");
return rows;
}
visible: shouldShow
@@ -162,7 +167,7 @@ Item {
visible: root.useGraphicMode
anchors.centerIn: parent
baseSize: Style.barFontSize
showPercentageText: true
showPercentageText: root.displayMode !== "graphic-clean"
vertical: root.isBarVertical
percentage: root.percent
ready: root.isReady
@@ -180,11 +185,13 @@ Item {
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: {
if (root.tooltipText) {
TooltipService.show(root, root.tooltipText, BarService.getTooltipDirection(root.screen?.name));
if (root.tooltipContent) {
TooltipService.show(root, root.tooltipContent, BarService.getTooltipDirection(root.screen?.name));
}
tooltipRefreshTimer.start();
}
onExited: {
tooltipRefreshTimer.stop();
TooltipService.hide();
}
onClicked: mouse => {
@@ -196,6 +203,17 @@ Item {
}
}
Timer {
id: tooltipRefreshTimer
interval: 1000
repeat: true
onTriggered: {
if (graphicMouseArea.containsMouse) {
TooltipService.updateText(root.tooltipContent);
}
}
}
// ==================== ICON MODE ====================
BarPill {
@@ -211,7 +229,7 @@ Item {
forceClose: root.displayMode === "icon-only" || !root.isReady
customBackgroundColor: root.isCharging ? Color.mPrimary : ((root.isLowBattery || root.isCriticalBattery) ? Color.mError : "transparent")
customTextIconColor: root.isCharging ? Color.mOnPrimary : ((root.isLowBattery || root.isCriticalBattery) ? Color.mOnError : "transparent")
tooltipText: root.tooltipText
tooltipText: root.tooltipContent
onClicked: openBatteryPanel()
onRightClicked: PanelService.showContextMenu(contextMenu, pill, screen)
@@ -61,6 +61,10 @@ ColumnLayout {
"key": "graphic",
"name": I18n.tr("bar.battery.display-mode-graphic")
},
{
"key": "graphic-clean",
"name": I18n.tr("bar.battery.display-mode-graphic-clean")
},
{
"key": "icon-hover",
"name": I18n.tr("bar.battery.display-mode-icon-hover")
+1 -1
View File
@@ -90,7 +90,7 @@ Singleton {
"hideWhenIdle": false
},
"Battery": {
"displayMode": "graphic",
"displayMode": "graphic-clean",
"deviceNativePath": "__default__",
"showPowerProfiles": false,
"showNoctaliaPerformance": false,
+8 -9
View File
@@ -27,11 +27,11 @@ Item {
property bool showPercentageText: true
property bool vertical: false
// Alternating state icon display (toggles between percentage and icon when charging/plugged)
// Alternating state icon display (toggles between percentage and icon when charging)
property bool showStateIcon: false
onHasStateIconChanged: {
if (!hasStateIcon)
onChargingChanged: {
if (!charging)
showStateIcon = false;
}
@@ -62,10 +62,9 @@ Item {
}
// Background color for empty portion (semi-transparent)
readonly property color emptyColor: Qt.alpha(baseColor, 0.7)
readonly property color emptyColor: Qt.alpha(baseColor, 0.66)
// State icon logic
readonly property bool hasStateIcon: (!ready || charging || pluggedIn)
readonly property string stateIcon: {
if (!ready)
return "x";
@@ -90,9 +89,9 @@ Item {
// Timer to alternate between percentage text and state icon when charging/plugged
Timer {
id: alternateTimer
interval: 3000
interval: 5000
repeat: true
running: root.hasStateIcon && root.ready
running: root.charging
onTriggered: root.showStateIcon = !root.showStateIcon
}
@@ -148,7 +147,7 @@ Item {
NText {
id: percentageText
visible: opacity > 0
opacity: root.showPercentageText && root.ready && !root.showStateIcon ? 1 : 0
opacity: root.showPercentageText && root.ready && (root.charging ? !root.showStateIcon : !root.pluggedIn) ? 1 : 0
x: batteryBody.x + Style.pixelAlignCenter(bodyBackground.width, width)
y: batteryBody.y + bodyBackground.y + Style.pixelAlignCenter(bodyBackground.height, height)
font.family: Settings.data.ui.fontFixed
@@ -174,7 +173,7 @@ Item {
NIcon {
id: stateIconOverlay
visible: opacity > 0
opacity: !root.ready || (root.hasStateIcon && root.showStateIcon) ? 1 : 0
opacity: !root.ready || (root.charging ? root.showStateIcon : root.pluggedIn) ? 1 : 0
x: batteryBody.x + Style.pixelAlignCenter(bodyBackground.width, width)
y: batteryBody.y + bodyBackground.y + Style.pixelAlignCenter(bodyBackground.height, height)
icon: root.stateIcon