diff --git a/Bar/Modules/Battery.qml b/Bar/Modules/Battery.qml index 899c829..acabd52 100644 --- a/Bar/Modules/Battery.qml +++ b/Bar/Modules/Battery.qml @@ -1,12 +1,13 @@ import QtQuick +import Quickshell import Quickshell.Services.UPower import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings Item { id: batteryWidget - + property var battery: UPower.displayDevice property bool isReady: battery && battery.ready && battery.isLaptopBattery && battery.isPresent property real percent: isReady ? (battery.percentage * 100) : 0 @@ -15,81 +16,63 @@ Item { // Choose icon based on charge and charging state function batteryIcon() { - if (!show) return ""; - - // Show charging icons with lightning when charging - if (charging) { - if (percent >= 95) return "battery_charging_full"; - if (percent >= 80) return "battery_charging_80"; - if (percent >= 60) return "battery_charging_60"; - if (percent >= 50) return "battery_charging_50"; - if (percent >= 30) return "battery_charging_30"; - if (percent >= 20) return "battery_charging_20"; - return "battery_charging_20"; // Use charging_20 for very low battery - } - - // Regular battery icons when not charging - if (percent >= 95) return "battery_full"; - if (percent >= 80) return "battery_80"; - if (percent >= 60) return "battery_60"; - if (percent >= 50) return "battery_50"; - if (percent >= 30) return "battery_30"; - if (percent >= 20) return "battery_20"; - return "battery_alert"; + if (!show) + return ""; + + if (charging) + return "battery_android_bolt"; + + if (percent >= 95) + return "battery_android_full"; + + var step = Math.round(percent / (100 / 6)); + return "battery_android_" + step } visible: isReady && battery.isLaptopBattery - width: 22 - height: 36 + width: pill.width + height: pill.height - RowLayout { - anchors.fill: parent - spacing: 4 - visible: show - Item { - height: 22 - width: 22 - Text { - text: batteryIcon() - font.family: "Material Symbols Outlined" - font.pixelSize: 14 - color: charging ? Theme.accentPrimary : Theme.textPrimary - verticalAlignment: Text.AlignVCenter - anchors.centerIn: parent + PillIndicator { + id: pill + icon: batteryIcon() + text: Math.round(batteryWidget.percent) + "%" + pillColor: Theme.surfaceVariant + iconCircleColor: Theme.accentPrimary + textColor: charging ? Theme.accentPrimary : Theme.textPrimary + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: { + pill.show(); + batteryTooltip.tooltipVisible = true; } - MouseArea { - id: batteryMouseArea - anchors.fill: parent - hoverEnabled: true - onEntered: batteryWidget.containsMouse = true - onExited: batteryWidget.containsMouse = false - cursorShape: Qt.PointingHandCursor + onExited: { + pill.hide(); + batteryTooltip.tooltipVisible = false; } } - } - - property bool containsMouse: false - - StyledTooltip { - id: batteryTooltip - text: { - let lines = []; - if (batteryWidget.isReady) { - lines.push(batteryWidget.charging ? "Charging" : "Discharging"); - lines.push(Math.round(batteryWidget.percent) + "%"); - if (batteryWidget.battery.changeRate !== undefined) - lines.push("Rate: " + batteryWidget.battery.changeRate.toFixed(2) + " W"); - if (batteryWidget.battery.timeToEmpty > 0) - lines.push("Time left: " + Math.floor(batteryWidget.battery.timeToEmpty / 60) + " min"); - if (batteryWidget.battery.timeToFull > 0) - lines.push("Time to full: " + Math.floor(batteryWidget.battery.timeToFull / 60) + " min"); - if (batteryWidget.battery.healthPercentage !== undefined) - lines.push("Health: " + Math.round(batteryWidget.battery.healthPercentage) + "%"); + StyledTooltip { + id: batteryTooltip + text: { + let lines = []; + if (batteryWidget.isReady) { + lines.push(batteryWidget.charging ? "Charging" : "Discharging"); + lines.push(Math.round(batteryWidget.percent) + "%"); + if (batteryWidget.battery.changeRate !== undefined) + lines.push("Rate: " + batteryWidget.battery.changeRate.toFixed(2) + " W"); + if (batteryWidget.battery.timeToEmpty > 0) + lines.push("Time left: " + Math.floor(batteryWidget.battery.timeToEmpty / 60) + " min"); + if (batteryWidget.battery.timeToFull > 0) + lines.push("Time to full: " + Math.floor(batteryWidget.battery.timeToFull / 60) + " min"); + if (batteryWidget.battery.healthPercentage !== undefined) + lines.push("Health: " + Math.round(batteryWidget.battery.healthPercentage) + "%"); + } + return lines.join("\n"); } - return lines.join("\n"); + tooltipVisible: false + targetItem: pill + delay: 1500 } - tooltipVisible: batteryWidget.containsMouse - targetItem: batteryWidget - delay: 200 } -} \ No newline at end of file +} diff --git a/Bar/Modules/Brightness.qml b/Bar/Modules/Brightness.qml index 5aa5fd7..c36f291 100644 --- a/Bar/Modules/Brightness.qml +++ b/Bar/Modules/Brightness.qml @@ -13,6 +13,7 @@ Item { property bool isSettingBrightness: false property bool hasPendingSet: false property int pendingSetValue: -1 + property bool firstChange: true width: pill.width height: pill.height @@ -30,7 +31,13 @@ Item { previousBrightness = brightness brightness = val pill.text = brightness + "%" - pill.show() + + if (firstChange) { + firstChange = false; + } + else { + pill.show() + } } } } @@ -94,14 +101,19 @@ Item { iconCircleColor: Theme.accentPrimary iconTextColor: Theme.backgroundPrimary textColor: Theme.textPrimary + MouseArea { anchors.fill: parent hoverEnabled: true onEntered: { getBrightness() brightnessTooltip.tooltipVisible = true + pill.show() + } + onExited: { + brightnessTooltip.tooltipVisible = false + pill.hide() } - onExited: brightnessTooltip.tooltipVisible = false onWheel: function(wheel) { const delta = wheel.angleDelta.y > 0 ? 5 : -5 @@ -114,14 +126,11 @@ Item { text: "Brightness: " + brightness + "%" tooltipVisible: false targetItem: pill - delay: 200 + delay: 1500 } } Component.onCompleted: { getBrightness() - if (brightness >= 0) { - pill.show() - } } } \ No newline at end of file diff --git a/Bar/Modules/Volume.qml b/Bar/Modules/Volume.qml index e07f0b9..860ac83 100644 --- a/Bar/Modules/Volume.qml +++ b/Bar/Modules/Volume.qml @@ -8,6 +8,7 @@ Item { id: volumeDisplay property var shell property int volume: 0 + property bool firstChange: true width: pillIndicator.width height: pillIndicator.height @@ -23,13 +24,14 @@ Item { iconCircleColor: Theme.accentPrimary iconTextColor: Theme.backgroundPrimary textColor: Theme.textPrimary + autoHide: true StyledTooltip { id: volumeTooltip text: "Volume: " + volume + "%\nScroll up/down to change volume.\nLeft click to open the input/output selection." tooltipVisible: !ioSelector.visible && volumeDisplay.containsMouse targetItem: pillIndicator - delay: 200 + delay: 1500 } MouseArea { @@ -57,7 +59,13 @@ Item { pillIndicator.icon = shell.defaultAudioSink && shell.defaultAudioSink.audio && shell.defaultAudioSink.audio.muted ? "volume_off" : (volume === 0 ? "volume_off" : (volume < 30 ? "volume_down" : "volume_up")); - pillIndicator.show(); + + if (firstChange) { + firstChange = false + } + else { + pillIndicator.show(); + } } } } @@ -66,7 +74,6 @@ Item { Component.onCompleted: { if (shell && shell.volume !== undefined) { volume = Math.max(0, Math.min(100, shell.volume)); - pillIndicator.show(); } } @@ -75,8 +82,16 @@ Item { hoverEnabled: true acceptedButtons: Qt.NoButton propagateComposedEvents: true - onEntered: volumeDisplay.containsMouse = true - onExited: volumeDisplay.containsMouse = false + onEntered: { + volumeDisplay.containsMouse = true + pillIndicator.autoHide = false; + pillIndicator.show() + } + onExited: { + volumeDisplay.containsMouse = false + pillIndicator.autoHide = true; + pillIndicator.hide() + } cursorShape: Qt.PointingHandCursor onWheel: (wheel) => { if (!shell) return; diff --git a/Components/PillIndicator.qml b/Components/PillIndicator.qml index e87d4af..4219931 100644 --- a/Components/PillIndicator.qml +++ b/Components/PillIndicator.qml @@ -15,6 +15,7 @@ Item { property int pillHeight: 22 property int iconSize: 22 property int pillPaddingHorizontal: 14 + property bool autoHide: false // Internal state property bool showPill: false @@ -24,8 +25,8 @@ Item { readonly property int pillOverlap: iconSize / 2 readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap) - signal shown() - signal hidden() + signal shown + signal hidden width: iconSize + (showPill ? maxPillWidth - pillOverlap : 0) height: pillHeight @@ -54,11 +55,17 @@ Item { Behavior on width { enabled: showAnim.running || hideAnim.running - NumberAnimation { duration: 250; easing.type: Easing.OutCubic } + NumberAnimation { + duration: 250 + easing.type: Easing.OutCubic + } } Behavior on opacity { enabled: showAnim.running || hideAnim.running - NumberAnimation { duration: 250; easing.type: Easing.OutCubic } + NumberAnimation { + duration: 250 + easing.type: Easing.OutCubic + } } } @@ -73,7 +80,10 @@ Item { anchors.right: parent.right Behavior on color { - ColorAnimation { duration: 200; easing.type: Easing.InOutQuad } + ColorAnimation { + duration: 200 + easing.type: Easing.InOutQuad + } } Text { @@ -106,11 +116,11 @@ Item { easing.type: Easing.OutCubic } onStarted: { - showPill = true + showPill = true; } onStopped: { - delayedHideAnim.start() - shown() + delayedHideAnim.start(); + shown(); } } @@ -118,8 +128,13 @@ Item { SequentialAnimation { id: delayedHideAnim running: false - PauseAnimation { duration: 2500 } - ScriptAction { script: if (shouldAnimateHide) hideAnim.start() } + PauseAnimation { + duration: 2500 + } + ScriptAction { + script: if (shouldAnimateHide) + hideAnim.start() + } } // Hide animation @@ -143,27 +158,27 @@ Item { easing.type: Easing.InCubic } onStopped: { - showPill = false - shouldAnimateHide = false - hidden() + showPill = false; + shouldAnimateHide = false; + hidden(); } } // Exposed functions function show() { if (!showPill) { - shouldAnimateHide = true - showAnim.start() + shouldAnimateHide = autoHide; + showAnim.start(); } else { // Reset hide timer if already shown - hideAnim.stop() - delayedHideAnim.restart() + hideAnim.stop(); + delayedHideAnim.restart(); } } function hide() { if (showPill) { - hideAnim.start() + hideAnim.start(); } } -} \ No newline at end of file +}