diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index 46f5701..30fc414 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -8,21 +8,57 @@ import qs.Widgets Item { id: root - property ShellScreen screen property real scaling: ScalingService.scale(screen) - implicitWidth: pill.width implicitHeight: pill.height + // Track if we've already notified to avoid spam + property bool hasNotifiedLowBattery: false + + // Helper to evaluate and possibly notify + function maybeNotify(percent, charging) { + const p = Math.round(percent) + if (!charging && p <= 15 && !root.hasNotifiedLowBattery) { + Quickshell.execDetached( + ["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`]) + root.hasNotifiedLowBattery = true + } + if (p > 20 || charging) { + root.hasNotifiedLowBattery = false + } + } + + // Watch for battery changes + Connections { + target: UPower.displayDevice + function onPercentageChanged() { + let battery = UPower.displayDevice + let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent + let percent = isReady ? (battery.percentage * 100) : 0 + let charging = isReady ? battery.state === UPowerDeviceState.Charging : false + + root.maybeNotify(percent, charging) + } + + function onStateChanged() { + let battery = UPower.displayDevice + let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent + let charging = isReady ? battery.state === UPowerDeviceState.Charging : false + + // Reset notification flag when charging starts + if (charging) { + root.hasNotifiedLowBattery = false + } + } + } + NPill { id: pill - // Test mode property bool testMode: false - property int testPercent: 49 + property int testPercent: 20 property bool testCharging: false - property var battery: UPower.displayDevice property bool isReady: testMode ? true : (battery && battery.ready && battery.isLaptopBattery && battery.isPresent) property real percent: testMode ? testPercent : (isReady ? (battery.percentage * 100) : 0) @@ -30,16 +66,12 @@ Item { // Choose icon based on charge and charging state function batteryIcon() { - if (!isReady || !battery.isLaptopBattery) return "battery_android_alert" - if (charging) return "battery_android_bolt" - if (percent >= 95) return "battery_android_full" - // Hardcoded battery symbols if (percent >= 85) return "battery_android_6" @@ -60,28 +92,26 @@ Item { icon: batteryIcon() text: (isReady && battery.isLaptopBattery) ? Math.round(percent) + "%" : "-" textColor: charging ? Color.mPrimary : Color.mOnSurface - forceOpen: isReady && battery.isLaptopBattery && Settings.data.bar.alwaysShowBatteryPercentage - disableOpen: (!isReady || !battery.isLaptopBattery) + iconCircleColor: Color.mPrimary + collapsedIconColor: Color.mOnSurface + autoHide: false + forceOpen: isReady && (testMode || battery.isLaptopBattery) && Settings.data.bar.alwaysShowBatteryPercentage + disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery)) tooltipText: { let lines = [] - if (testMode) { lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(12345)) return lines.join("\n") } - if (!isReady || !battery.isLaptopBattery) { return "No Battery Detected" } - if (battery.timeToEmpty > 0) { lines.push("Time Left: " + Time.formatVagueHumanReadableDuration(battery.timeToEmpty)) } - if (battery.timeToFull > 0) { lines.push("Time Until Full: " + Time.formatVagueHumanReadableDuration(battery.timeToFull)) } - if (battery.changeRate !== undefined) { const rate = battery.changeRate if (rate > 0) { @@ -95,7 +125,6 @@ Item { } else { lines.push(charging ? "Charging" : "Discharging") } - if (battery.healthPercentage !== undefined && battery.healthPercentage > 0) { lines.push("Health: " + Math.round(battery.healthPercentage) + "%") } diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index a6d554a..2d968be 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -203,38 +203,7 @@ Variants { elide: Text.ElideRight } - // Notification actions - RowLayout { - visible: model.actions && model.actions.length > 0 - spacing: Style.marginXS * scaling - Layout.fillWidth: true - Layout.topMargin: Style.marginS * scaling - - Repeater { - model: model.actions || [] - delegate: NPill { - text: modelData.text || modelData.identifier || "Action" - icon: modelData.identifier || "" - tooltipText: modelData.text || modelData.identifier || "Action" - sizeMultiplier: 0.7 - Layout.fillWidth: true - forceOpen: true - - // Style action buttons differently - pillColor: Color.mPrimary - textColor: Color.mOnPrimary - iconCircleColor: Color.mPrimary - iconTextColor: Color.mOnPrimary - - onClicked: { - // Invoke the notification action - modelData.invoke() - // Animate out the notification after action - animateOut() - } - } - } - } + // Actions removed } NIconButton { diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 501abdd..0b43142 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -29,8 +29,7 @@ Singleton { Logger.log("Wallpapers", "Listing wallpapers") scanning = true wallpaperList = [] - // Unsetting, then setting the folder will re-trigger the parsing! - folderModel.folder = "" + // Set the folder directly to avoid model reset issues folderModel.folder = "file://" + (Settings.data.wallpaper.directory !== undefined ? Settings.data.wallpaper.directory : "") } diff --git a/Widgets/NText.qml b/Widgets/NText.qml index 7436c54..e6986fb 100644 --- a/Widgets/NText.qml +++ b/Widgets/NText.qml @@ -10,4 +10,7 @@ Text { font.pointSize: Style.fontSizeM * scaling font.weight: Style.fontWeightMedium color: Color.mOnSurface + renderType: Text.QtRendering + font.hintingPreference: Font.PreferNoHinting + font.kerning: true } diff --git a/Widgets/NWidgetLoader.qml b/Widgets/NWidgetLoader.qml index 600b5e7..e0ec294 100644 --- a/Widgets/NWidgetLoader.qml +++ b/Widgets/NWidgetLoader.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import qs.Services +import qs.Commons Item { id: root