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 1/3] 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) } } From 33e797707795c3627124a650772f016b93abfdbf Mon Sep 17 00:00:00 2001 From: shouya <526598+shouya@users.noreply.github.com> Date: Sat, 8 Nov 2025 22:55:15 +0900 Subject: [PATCH 2/3] add i18n for non-english languages --- Assets/Translations/de.json | 4 ++-- Assets/Translations/es.json | 4 ++-- Assets/Translations/fr.json | 4 ++-- Assets/Translations/pt.json | 4 ++-- Assets/Translations/tr.json | 4 ++-- Assets/Translations/uk-UA.json | 4 ++-- Assets/Translations/zh-CN.json | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 0650ce55..348d2a60 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -1813,10 +1813,10 @@ "close": "Schließen", "connect-disconnect-devices": "Linksklick zum Verbinden. Rechtsklick zum Vergessen.", "delete-notification": "Benachrichtigung löschen", - "disable-keep-awake": "Wach halten deaktivieren", + "disable-keep-awake": "Klicken, um Wach halten zu deaktivieren.\nScrollen, um Zeitlimit anzupassen.", "do-not-disturb-disabled": "'Nicht stören' deaktiviert", "do-not-disturb-enabled": "'Nicht stören' aktiviert", - "enable-keep-awake": "Wach halten aktivieren", + "enable-keep-awake": "Klicken, um Wach halten zu aktivieren.\nScrollen, um Zeitlimit anzupassen.", "forget-network": "Netzwerk vergessen", "home": "Home", "input-muted": "Audio-Eingabe stummschalten", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 72b0ddc5..794263fd 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -1813,10 +1813,10 @@ "close": "Cerrar", "connect-disconnect-devices": "Clic izquierdo para conectar. Clic derecho para olvidar.", "delete-notification": "Eliminar notificación", - "disable-keep-awake": "Desactivar mantener despierto", + "disable-keep-awake": "Haz clic para desactivar mantener despierto.\nDesplázate para ajustar el tiempo de espera.", "do-not-disturb-disabled": "'No molestar' desactivado", "do-not-disturb-enabled": "'No molestar' activado", - "enable-keep-awake": "Activar mantener despierto", + "enable-keep-awake": "Haz clic para activar mantener despierto.\nDesplázate para ajustar el tiempo de espera.", "forget-network": "Olvidar red", "home": "Inicio", "input-muted": "Silenciar entrada de audio", diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 15ee667c..98f28c28 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -1813,10 +1813,10 @@ "close": "Fermer", "connect-disconnect-devices": "Clic gauche pour connecter. Clic droit pour oublier.", "delete-notification": "Supprimer la notification", - "disable-keep-awake": "Désactiver le mode 'rester éveillé'", + "disable-keep-awake": "Cliquez pour désactiver le mode 'rester éveillé'.\nFaites défiler pour ajuster le délai.", "do-not-disturb-disabled": "'Ne pas déranger' désactivé", "do-not-disturb-enabled": "'Ne pas déranger' activé", - "enable-keep-awake": "Activer le mode 'rester éveillé'", + "enable-keep-awake": "Cliquez pour activer le mode 'rester éveillé'.\nFaites défiler pour ajuster le délai.", "forget-network": "Oublier le réseau", "home": "Accueil", "input-muted": "Couper l'entrée audio", diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 0d4348bd..a5d738c8 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -1813,10 +1813,10 @@ "close": "Fechar", "connect-disconnect-devices": "Clique esquerdo para conectar. Clique direito para esquecer.", "delete-notification": "Excluir notificação", - "disable-keep-awake": "Desativar 'manter acordado'", + "disable-keep-awake": "Clique para desativar 'manter acordado'.\nRole para ajustar o tempo limite.", "do-not-disturb-disabled": "'Não perturbe' desativado", "do-not-disturb-enabled": "'Não perturbe' ativado", - "enable-keep-awake": "Ativar 'manter acordado'", + "enable-keep-awake": "Clique para ativar 'manter acordado'.\nRole para ajustar o tempo limite.", "forget-network": "Esquecer rede", "home": "Início", "input-muted": "Silenciar entrada de áudio", diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json index fc7639ce..64a25003 100644 --- a/Assets/Translations/tr.json +++ b/Assets/Translations/tr.json @@ -1813,10 +1813,10 @@ "close": "Kapat", "connect-disconnect-devices": "Bağlanmak için sol tık. Unutmak için sağ tık.", "delete-notification": "Bildiriyi sil", - "disable-keep-awake": "Uyanık kalmayı devre dışı bırak", + "disable-keep-awake": "Uyanık kalmayı devre dışı bırakmak için tıklayın.\nZaman aşımını ayarlamak için kaydırın.", "do-not-disturb-disabled": "'Rahatsız etme' devre dışı", "do-not-disturb-enabled": "'Rahatsız etme' etkin", - "enable-keep-awake": "Uyanık kalmayı etkinleştir", + "enable-keep-awake": "Uyanık kalmayı etkinleştirmek için tıklayın.\nZaman aşımını ayarlamak için kaydırın.", "forget-network": "Ağı unut", "home": "Ana Sayfa", "input-muted": "Giriş sessizliğini değiştir", diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json index 542a4836..f1185e15 100644 --- a/Assets/Translations/uk-UA.json +++ b/Assets/Translations/uk-UA.json @@ -1813,10 +1813,10 @@ "close": "Закрити", "connect-disconnect-devices": "Лівий клік для підключення. Правий клік для забування.", "delete-notification": "Видалити сповіщення", - "disable-keep-awake": "Вимкнути режим неспання", + "disable-keep-awake": "Клацніть, щоб вимкнути режим неспання.\nПрокрутіть, щоб налаштувати тайм-аут.", "do-not-disturb-disabled": "'Не турбувати' вимкнено", "do-not-disturb-enabled": "'Не турбувати' увімкнено", - "enable-keep-awake": "Увімкнути режим неспання", + "enable-keep-awake": "Клацніть, щоб увімкнути режим неспання.\nПрокрутіть, щоб налаштувати тайм-аут.", "forget-network": "Забути мережу", "home": "Додому", "input-muted": "Перемкнути вимкнення входу", diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 8a9e227d..7d43b078 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -1813,10 +1813,10 @@ "close": "关闭", "connect-disconnect-devices": "左键点击连接。右键点击忘记。", "delete-notification": "删除通知", - "disable-keep-awake": "禁用保持唤醒", + "disable-keep-awake": "单击以禁用保持唤醒。\n滚动以调整超时。", "do-not-disturb-disabled": "'勿扰模式'已禁用", "do-not-disturb-enabled": "'勿扰模式'已启用", - "enable-keep-awake": "启用保持唤醒", + "enable-keep-awake": "单击以启用保持唤醒。\n滚动以调整超时。", "forget-network": "忘记网络", "home": "主目录", "input-muted": "静音输入设备", From 0f360859d090314db7a42a5dea651daa6b6b135d Mon Sep 17 00:00:00 2001 From: shouya <526598+shouya@users.noreply.github.com> Date: Sat, 8 Nov 2025 23:01:21 +0900 Subject: [PATCH 3/3] fix flickering text when scrolling to zero while hovering --- Modules/Bar/Widgets/KeepAwake.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index e8e73b55..0aa69f5c 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -48,6 +48,7 @@ Item { I18n.tr("tooltips.enable-keep-awake") onClicked: IdleInhibitorService.manualToggle() forceOpen: IdleInhibitorService.timeout !== null + forceClose: IdleInhibitorService.timeout == null onWheel: function (delta) { var sign = delta > 0 ? 1 : -1 // the offset makes scrolling down feel symmetrical to scrolling up