From d85f426b150bd36aa47d5e67fc7ab8b99689c933 Mon Sep 17 00:00:00 2001 From: shouya <526598+shouya@users.noreply.github.com> Date: Tue, 21 Oct 2025 01:35:40 +0900 Subject: [PATCH] allow setting a timeout to manual sleep inhibitor --- Assets/Translations/en.json | 4 +- Modules/Bar/Widgets/KeepAwake.qml | 67 ++++++++++++--- Services/Power/IdleInhibitorService.qml | 105 ++++++++++++++++++++++-- Widgets/NIconButton.qml | 2 + 4 files changed, 158 insertions(+), 20 deletions(-) diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 7901eef4..422290b2 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -1813,10 +1813,10 @@ "close": "Close", "connect-disconnect-devices": "Left click to connect. Right click to forget.", "delete-notification": "Delete notification", - "disable-keep-awake": "Disable keep awake", + "disable-keep-awake": "Click to disable keep awake.\nScroll to adjust timeout.", "do-not-disturb-disabled": "'Do not disturb' disabled", "do-not-disturb-enabled": "'Do not disturb' enabled", - "enable-keep-awake": "Enable keep awake", + "enable-keep-awake": "Click to enable keep awake.\nScroll to adjust timeout.", "forget-network": "Forget network", "home": "Home", "input-muted": "Toggle input mute", diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index 46f975d6..e8e73b55 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -5,21 +5,64 @@ import qs.Commons import qs.Services.Power import qs.Services.UI import qs.Widgets +import qs.Modules.Bar.Extras -NIconButton { +Item { id: root property ShellScreen screen - baseSize: Style.capsuleHeight - applyUiScale: false - density: Settings.data.bar.density - icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" - tooltipText: IdleInhibitorService.isInhibited ? I18n.tr("tooltips.disable-keep-awake") : I18n.tr("tooltips.enable-keep-awake") - tooltipDirection: BarService.getTooltipDirection() - colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) - colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface - colorBorder: Color.transparent - colorBorderHover: Color.transparent - onClicked: IdleInhibitorService.manualToggle() + // Widget properties passed from Bar.qml for per-instance settings + property string widgetId: "" + property string section: "" + property int sectionWidgetIndex: -1 + property int sectionWidgetsCount: 0 + + property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId] + property var widgetSettings: { + if (section && sectionWidgetIndex >= 0) { + var widgets = Settings.data.bar.widgets[section] + if (widgets && sectionWidgetIndex < widgets.length) { + return widgets[sectionWidgetIndex] + } + } + return {} + } + + readonly property bool isBarVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right" + + implicitWidth: pill.width + implicitHeight: pill.height + + BarPill { + id: pill + + text: IdleInhibitorService.timeout == null ? "" : + Time.formatVagueHumanReadableDuration(IdleInhibitorService.timeout) + + density: Settings.data.bar.density + oppositeDirection: BarService.getPillDirection(root) + icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" + tooltipText: IdleInhibitorService.isInhibited ? + I18n.tr("tooltips.disable-keep-awake") : + I18n.tr("tooltips.enable-keep-awake") + onClicked: IdleInhibitorService.manualToggle() + forceOpen: IdleInhibitorService.timeout !== null + onWheel: function (delta) { + var sign = delta > 0 ? 1 : -1 + // the offset makes scrolling down feel symmetrical to scrolling up + var timeout = IdleInhibitorService.timeout - (delta < 0 ? 60 : 0) + if (timeout == null || timeout < 600) { + delta = 60 // <= 10m, increment at 1m interval + } else if (timeout >= 600 && timeout < 1800) { + delta = 300 // >= 10m, increment at 5m interval + } else if (timeout >= 1800 && timeout < 3600) { + delta = 600 // >= 30m, increment at 10m interval + } else if (timeout >= 3600) { + delta = 1800 // > 1h, increment at 30m interval + } + + IdleInhibitorService.changeTimeout(delta * sign) + } + } } diff --git a/Services/Power/IdleInhibitorService.qml b/Services/Power/IdleInhibitorService.qml index 80aaba3f..dc670e25 100644 --- a/Services/Power/IdleInhibitorService.qml +++ b/Services/Power/IdleInhibitorService.qml @@ -12,6 +12,7 @@ Singleton { property bool isInhibited: false property string reason: I18n.tr("system.user-requested") property var activeInhibitors: [] + property var timeout: null // in seconds // Different inhibitor strategies property string strategy: "systemd" // "systemd", "wayland", or "auto" @@ -152,21 +153,113 @@ Singleton { } } + Timer { + id: inhibitorTimeout + repeat: true + interval: 1000 // 1 second + onTriggered: function () { + if (timeout == null) { + inhibitorTimeout.stop() + return + } + + timeout -= 1 + if (timeout <= 0) { + removeManualInhibitor() + return + } + } + } + // Manual toggle for user control function manualToggle() { + // clear any existing timeout + timeout = null if (activeInhibitors.includes("manual")) { - removeInhibitor("manual") - ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.disabled"), "keep-awake-off") - Logger.i("IdleInhibitor", "Manual inhibition disabled") + removeManualInhibitor() return false } else { - addInhibitor("manual", "Manually activated by user") - ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.enabled"), "keep-awake-on") - Logger.i("IdleInhibitor", "Manual inhibition enabled (will reset on next session)") + addManualInhibitor(null) return true } } + function changeTimeout(delta) { + if (timeout == null && delta < 0) { + // no inhibitor, ignored + return; + } + + if (timeout == null && delta > 0) { + // enable manual inhibitor and set timeout + addManualInhibitor(timeout + delta); + return; + } + + if (timeout + delta <= 0) { + // disable manual inhibitor + removeManualInhibitor(); + return; + } + + if (timeout + delta > 0) { + // change timeout + addManualInhibitor(timeout + delta); + return; + } + } + + function removeManualInhibitor() { + if (timeout !== null) { + timeout = null + if (inhibitorTimeout.running) { + inhibitorTimeout.stop() + } + } + + if (activeInhibitors.includes("manual")) { + removeInhibitor("manual") + ToastService.showNotice( + I18n.tr("tooltips.keep-awake"), + I18n.tr("toast.keep-awake.disabled"), + "keep-awake-off" + ) + Logger.i("IdleInhibitor", "Manual inhibition disabled") + } + } + + function addManualInhibitor(timeoutSec) { + if (!activeInhibitors.includes("manual")) { + addInhibitor("manual", "Manually activated by user") + ToastService.showNotice( + I18n.tr("tooltips.keep-awake"), + I18n.tr("toast.keep-awake.enabled"), + "keep-awake-on" + ) + } + + if (timeoutSec === null && timeout === null) { + Logger.i("IdleInhibitor", "Manual inhibition enabled") + return + } else if (timeoutSec !== null && timeout === null) { + timeout = timeoutSec + inhibitorTimeout.start() + Logger.i("IdleInhibitor", "Manual inhibition enabled with timeout:", timeoutSec) + return + } else if (timeoutSec !== null && timeout !== null) { + timeout = timeoutSec + Logger.i("IdleInhibitor", "Manual inhibition timeout changed to:", timeoutSec) + return + } else if (timeoutSec === null && timeout !== null) { + timeout = null + inhibitorTimeout.stop() + Logger.i("IdleInhibitor", "Manual inhibition timeout cleared") + return + } + } + + + // Clean up on shutdown Component.onDestruction: { stopInhibition() diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml index c72d7d38..4dfb1273 100644 --- a/Widgets/NIconButton.qml +++ b/Widgets/NIconButton.qml @@ -30,6 +30,7 @@ Rectangle { signal clicked signal rightClicked signal middleClicked + signal wheel implicitWidth: applyUiScale ? Math.round(baseSize * Style.uiScaleRatio) : Math.round(baseSize) implicitHeight: applyUiScale ? Math.round(baseSize * Style.uiScaleRatio) : Math.round(baseSize) @@ -108,5 +109,6 @@ Rectangle { root.middleClicked() } } + onWheel: wheel => root.wheel(wheel.angleDelta.y) } }