Watch
1
0
Fork
You've already forked souveraine
0

shell: the dim is a grace before the lock, not a second absolute timer

This commit is contained in:
Fimeg 2026-07-26 15:09:17 -04:00
commit 4f113ad62d
3 changed files with 71 additions and 9 deletions

View file

@ -478,8 +478,21 @@ Singleton {
// Native idle-notify remains opt-in until verified on the
// Pixel compositor; hypridle is the current adapter.
property bool nativeCoordinatorEnabled: false
property int dimAfterSeconds: 120
// The lock timer is the one the user sets. The dim is
// expressed RELATIVE to it how long before the lock the
// screen starts fading so the two can never cross and
// moving one moves the other. They were independent
// absolutes (120 and 300), which meant picking a dim
// longer than the lock silently produced a dim that never
// fired. sessiond's lock-screen dim has always been
// relative (`dim_at = budget - dim_grace`); this is the
// shell saying the same thing the same way.
//
// Default 180 reproduces the old pair exactly: 300 - 180 =
// 120. An existing config.json keeps its lockAfterSeconds
// and picks this default up, so nobody's timing changes.
property int lockAfterSeconds: 300
property int dimBeforeLockSeconds: 180
// Backlight level while Dimmed; Active restores the
// brightnessctl-saved value.
property string dimBrightness: "30%"

View file

@ -58,12 +58,32 @@ ContentPage {
{ displayName: Translation.tr("5 min"), icon: "timer", value: 300 },
{ displayName: Translation.tr("10 min"), icon: "timer", value: 600 }
]
// How long before the lock the dim starts. Wider range than the
// lock-screen grace below: this one has a whole idle-to-lock budget to sit
// inside, and 300 with a 10-minute lock is Casey's "the 15s dim starts at
// 9:45" shape at a larger scale.
readonly property var dimGracePresets: [
{ displayName: Translation.tr("15s"), icon: "brightness_medium", value: 15 },
{ displayName: Translation.tr("30s"), icon: "brightness_medium", value: 30 },
{ displayName: Translation.tr("1 min"), icon: "brightness_medium", value: 60 },
{ displayName: Translation.tr("3 min"), icon: "brightness_medium", value: 180 },
{ displayName: Translation.tr("5 min"), icon: "brightness_medium", value: 300 }
]
readonly property var gracePresets: [
{ displayName: Translation.tr("5s"), icon: "brightness_medium", value: 5 },
{ displayName: Translation.tr("10s"), icon: "brightness_medium", value: 10 },
{ displayName: Translation.tr("20s"), icon: "brightness_medium", value: 20 }
]
// "2 min" reads better than "120 s" in a sentence about when things happen.
function clockText(secs) {
if (secs >= 60 && secs % 60 === 0)
return Translation.tr("%1 min").arg(secs / 60);
if (secs >= 60)
return Translation.tr("%1 min %2 s").arg(Math.floor(secs / 60)).arg(secs % 60);
return Translation.tr("%1 s").arg(secs);
}
readonly property var stageNames: [
Translation.tr("Active"),
Translation.tr("Dimmed"),
@ -142,15 +162,6 @@ ContentPage {
// Config keys, effective only while the native coordinator is on.
// Presets rather than a seconds spinner, same reasoning as below.
ContentSubsectionLabel {
text: Translation.tr("Dim the screen after")
}
ConfigSelectionArray {
currentValue: Config.options.lock.idle.dimAfterSeconds
onSelected: v => Config.options.lock.idle.dimAfterSeconds = v
options: page.idlePresets
}
ContentSubsectionLabel {
text: Translation.tr("Lock the session after")
}
@ -160,6 +171,37 @@ ContentPage {
options: page.idlePresets
}
// Relative, not absolute. The two used to be independent seconds
// values and nothing stopped a dim set past the lock, which produced
// a dim that silently never fired. Choosing "how long before" makes
// the relationship the thing the user sets, so moving the lock moves
// the dim with it.
ContentSubsectionLabel {
text: Translation.tr("Start dimming this long before locking")
}
ConfigSelectionArray {
currentValue: Config.options.lock.idle.dimBeforeLockSeconds
onSelected: v => Config.options.lock.idle.dimBeforeLockSeconds = v
options: page.dimGracePresets
}
// Say what the pair actually does. A relationship the user has to
// compute in their head is one they will get wrong.
StyledText {
Layout.fillWidth: true
wrapMode: Text.WordWrap
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.smaller
text: {
const lock = Config.options.lock.idle.lockAfterSeconds;
const grace = Config.options.lock.idle.dimBeforeLockSeconds;
const dimAt = Math.max(1, Math.min(lock - 1, lock - grace));
return Translation.tr("Dims at %1, locks at %2.")
.arg(page.clockText(dimAt))
.arg(page.clockText(lock));
}
}
StyledText {
Layout.fillWidth: true
visible: !Config.options.lock.idle.nativeCoordinatorEnabled

View file

@ -118,7 +118,14 @@ Singleton {
IdleMonitor {
id: dimMonitor
enabled: root.nativeEnabled
timeout: Math.max(1, Config.options.lock.idle.dimAfterSeconds) * 1000
// Derived, never stored: the dim is a grace before the lock, so it
// cannot be set past it. Clamped to leave at least a second of dim
// a grace >= the lock budget would otherwise mean dimming before the
// user stopped touching the phone.
timeout: Math.max(1, Math.min(
Config.options.lock.idle.lockAfterSeconds - 1,
Config.options.lock.idle.lockAfterSeconds
- Config.options.lock.idle.dimBeforeLockSeconds)) * 1000
respectInhibitors: true
onIsIdleChanged: {
if (isIdle && !root.lockRequested && !Idle.inhibit) {