feat: add an animation speed setting

This commit is contained in:
Felitendo committed 2026-09-22 23:22:33 +02:00
1 parent dc0f680652
commit 71d01cd710
11 files changed
+156 -100

No files matched your search

+5 -3
View File
@@ -16,8 +16,9 @@ instead of a password. Before a face counts, it has to show a sign of life, so
holding up a photo of somebody is not enough.
A bubble at the top of the screen shows what is going on: it drops down when
the camera starts looking, the face in it looks around, and it turns into a
tick when it recognises somebody. It shows above the lock screen too.
the camera starts looking, the face in it looks around, and when it recognises
somebody two green rings spin and land around a tick. It shows above the lock
screen too. *Animation speed* makes all of it faster or slower.
Run without a command it shows an interactive menu. Everything it can be told
is reachable from there; the settings files behind it do not need to be edited
@@ -228,7 +229,8 @@ A laptop with its lid shut is not scanned (*Not while the lid is closed*).
# FILES
_~/.config/plasma-face-unlock/config_
This user's settings: the lock screen, the bubble. Written by the menu.
This user's settings: the lock screen, the bubble and its animation
speed. Written by the menu.
_/etc/plasma-face-unlock/config_
The system settings: camera, photo check, strictness, attention, scan
+12
View File
@@ -763,3 +763,15 @@ msgstr "Los geht's"
msgid "Done"
msgstr "Fertig"
#: src/lib/menu.sh
msgid "Animation speed"
msgstr "Animationsgeschwindigkeit"
#: src/lib/menu.sh
msgid "fast"
msgstr "schnell"
#: src/lib/menu.sh
msgid "slow"
msgstr "langsam"
+63 -51
View File
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.0\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/"
"issues\n"
"POT-Creation-Date: 2026-09-22 23:06+0200\n"
"POT-Creation-Date: 2026-09-22 23:12+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -55,8 +55,8 @@ msgstr ""
msgid "First, set up your face."
msgstr ""
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:348
#: src/lib/menu.sh:352 src/lib/menu.sh:358 src/lib/menu.sh:360
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:352
#: src/lib/menu.sh:356 src/lib/menu.sh:362 src/lib/menu.sh:364
msgid "Could not save the setting."
msgstr ""
@@ -84,16 +84,16 @@ msgstr ""
msgid "No face is set up yet."
msgstr ""
#: src/plasma-face-unlock:148 src/lib/menu.sh:167 src/lib/menu.sh:472
#: src/plasma-face-unlock:148 src/lib/menu.sh:167 src/lib/menu.sh:476
msgid "on"
msgstr ""
#: src/plasma-face-unlock:148 src/lib/menu.sh:169 src/lib/menu.sh:179
#: src/lib/menu.sh:473
#: src/lib/menu.sh:477
msgid "off"
msgstr ""
#: src/plasma-face-unlock:150 src/lib/menu.sh:490
#: src/plasma-face-unlock:150 src/lib/menu.sh:494
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr ""
@@ -123,7 +123,7 @@ msgstr ""
msgid "Turn it off (faces are kept)"
msgstr ""
#: src/plasma-face-unlock:176 src/lib/menu.sh:555
#: src/plasma-face-unlock:176 src/lib/menu.sh:559
msgid "Add a face"
msgstr ""
@@ -222,7 +222,7 @@ msgstr ""
msgid "The camera could not be used."
msgstr ""
#: src/lib/daemon.sh:111 src/lib/menu.sh:243
#: src/lib/daemon.sh:111 src/lib/menu.sh:246
msgid "The recognition models are missing. Reinstall the package."
msgstr ""
@@ -323,11 +323,11 @@ msgstr ""
msgid "[y/N]"
msgstr ""
#: src/lib/menu.sh:159 src/lib/menu.sh:392
#: src/lib/menu.sh:159 src/lib/menu.sh:396
msgid "ON"
msgstr ""
#: src/lib/menu.sh:161 src/lib/menu.sh:393
#: src/lib/menu.sh:161 src/lib/menu.sh:397
msgid "OFF"
msgstr ""
@@ -339,7 +339,7 @@ msgstr ""
msgid "basic (screens and phone edges)"
msgstr ""
#: src/lib/menu.sh:180
#: src/lib/menu.sh:180 src/lib/menu.sh:185
msgid "normal"
msgstr ""
@@ -359,132 +359,140 @@ msgstr ""
msgid "small pill with a lock"
msgstr ""
#: src/lib/menu.sh:185
#: src/lib/menu.sh:186
msgid "fast"
msgstr ""
#: src/lib/menu.sh:187
msgid "slow"
msgstr ""
#: src/lib/menu.sh:188
#, sh-printf-format
msgid "%s seconds"
msgstr ""
#: src/lib/menu.sh:186 src/lib/menu.sh:323
#: src/lib/menu.sh:189 src/lib/menu.sh:327
msgid "automatic"
msgstr ""
#: src/lib/menu.sh:203
#: src/lib/menu.sh:206
msgid "Face unlock"
msgstr ""
#: src/lib/menu.sh:207
#: src/lib/menu.sh:210
msgid "Service"
msgstr ""
#: src/lib/menu.sh:207
#: src/lib/menu.sh:210
msgid "not running"
msgstr ""
#: src/lib/menu.sh:209
#: src/lib/menu.sh:212
msgid "Turning face unlock on again starts it."
msgstr ""
#: src/lib/menu.sh:220 src/lib/menu.sh:222 src/lib/menu.sh:469
#: src/lib/menu.sh:556
#: src/lib/menu.sh:223 src/lib/menu.sh:225 src/lib/menu.sh:473
#: src/lib/menu.sh:560
msgid "Faces"
msgstr ""
#: src/lib/menu.sh:220
#: src/lib/menu.sh:223
msgid "none set up yet"
msgstr ""
#: src/lib/menu.sh:222
#: src/lib/menu.sh:225
msgid "all turned off"
msgstr ""
#: src/lib/menu.sh:228
#: src/lib/menu.sh:231
msgid "none found"
msgstr ""
#: src/lib/menu.sh:230 /tmp/tmp.FOzKaHKE4a/settings.sh:9
#: src/lib/menu.sh:233 /tmp/tmp.PgsbSPb4Sv/settings.sh:9
msgid "Camera"
msgstr ""
#: src/lib/menu.sh:232
#: src/lib/menu.sh:235
msgid "Lock screen"
msgstr ""
#: src/lib/menu.sh:233
#: src/lib/menu.sh:236
msgid "sudo"
msgstr ""
#: src/lib/menu.sh:234
#: src/lib/menu.sh:237
msgid "Admin prompts"
msgstr ""
#: src/lib/menu.sh:235 /tmp/tmp.FOzKaHKE4a/settings.sh:6
#: src/lib/menu.sh:238 /tmp/tmp.PgsbSPb4Sv/settings.sh:6
msgid "Photo check"
msgstr ""
#: src/lib/menu.sh:238
#: src/lib/menu.sh:241
msgid "Paused"
msgstr ""
#: src/lib/menu.sh:238
#: src/lib/menu.sh:241
#, sh-printf-format
msgid "for %d more minutes, or until the password is used"
msgstr ""
#: src/lib/menu.sh:240
#: src/lib/menu.sh:243
msgid "Last unlock"
msgstr ""
#: src/lib/menu.sh:319
#: src/lib/menu.sh:323
#, sh-printf-format
msgid "automatic (%s)"
msgstr ""
#: src/lib/menu.sh:329
#: src/lib/menu.sh:333
msgid "(infrared)"
msgstr ""
#: src/lib/menu.sh:333
#: src/lib/menu.sh:337
msgid "(not connected)"
msgstr ""
#: src/lib/menu.sh:389 src/lib/menu.sh:557
#: src/lib/menu.sh:393 src/lib/menu.sh:561
msgid "Settings"
msgstr ""
#: src/lib/menu.sh:390
#: src/lib/menu.sh:394
msgid "Up/Down: select, Space or Right: change, q: back"
msgstr ""
#: src/lib/menu.sh:391
#: src/lib/menu.sh:395
msgid "Settings marked * are for the whole computer and ask for your password."
msgstr ""
#: src/lib/menu.sh:470
#: src/lib/menu.sh:474
msgid "Up/Down: select, Space: on/off, r: rename, d: delete, a: add, q: back"
msgstr ""
#: src/lib/menu.sh:471
#: src/lib/menu.sh:475
msgid "No face is set up yet. Press a to add one."
msgstr ""
#: src/lib/menu.sh:516
#: src/lib/menu.sh:520
msgid "New name:"
msgstr ""
#: src/lib/menu.sh:523
#: src/lib/menu.sh:527
#, sh-printf-format
msgid "Delete \"%s\"?"
msgstr ""
#: src/lib/menu.sh:554
#: src/lib/menu.sh:558
msgid "Turn face unlock on or off"
msgstr ""
#: src/lib/menu.sh:558
#: src/lib/menu.sh:562
msgid "Try it"
msgstr ""
#: src/lib/menu.sh:559
#: src/lib/menu.sh:563
msgid "Quit"
msgstr ""
@@ -564,39 +572,43 @@ msgstr ""
msgid "Bubble style"
msgstr ""
#: src/lib/menu.sh
msgid "Animation speed"
msgstr ""
#: src/lib/menu.sh
msgid "Bubble for sudo and admin prompts too"
msgstr ""
#: src/agent/bubblecontroller.cpp:92
#: src/agent/bubblecontroller.cpp:98
msgid "Blink once"
msgstr ""
#: src/agent/bubblecontroller.cpp:94
#: src/agent/bubblecontroller.cpp:100
msgid "Look at the screen"
msgstr ""
#: src/agent/bubblecontroller.cpp:96
#: src/agent/bubblecontroller.cpp:102
msgid "Move closer"
msgstr ""
#: src/agent/bubblecontroller.cpp:98
#: src/agent/bubblecontroller.cpp:104
msgid "Too dark"
msgstr ""
#: src/agent/bubblecontroller.cpp:118
#: src/agent/bubblecontroller.cpp:124
msgid "Use your password"
msgstr ""
#: src/agent/bubblecontroller.cpp:124
#: src/agent/bubblecontroller.cpp:130
msgid "Not recognized"
msgstr ""
#: src/agent/bubblecontroller.cpp:126
#: src/agent/bubblecontroller.cpp:132
msgid "Try again and blink"
msgstr ""
#: src/agent/bubblecontroller.cpp:128
#: src/agent/bubblecontroller.cpp:134
msgid "Camera unavailable"
msgstr ""
+9 -3
View File
@@ -28,6 +28,12 @@ BubbleController::BubbleController(UserConfig *config, QObject *parent)
m_hide.setSingleShot(true);
connect(&m_hide, &QTimer::timeout, this, &BubbleController::dismiss);
connect(m_config, &UserConfig::changed, this, &BubbleController::styleChanged);
connect(m_config, &UserConfig::changed, this, &BubbleController::paceChanged);
}
qreal BubbleController::pace() const
{
return m_config->pace();
}
QString BubbleController::style() const
@@ -106,7 +112,7 @@ void BubbleController::succeeded()
}
setMessage({});
setPhase(QStringLiteral("success"));
m_hide.start(SuccessHoldMs);
m_hide.start(int(SuccessHoldMs * pace()));
}
void BubbleController::failed(const QString &reason, qint64 lockout)
@@ -117,7 +123,7 @@ void BubbleController::failed(const QString &reason, qint64 lockout)
if (lockout > 0 || reason == u"lockout") {
setMessage(i18n("Use your password"));
setPhase(QStringLiteral("lockout"));
m_hide.start(LockoutHoldMs);
m_hide.start(int(LockoutHoldMs * pace()));
return;
}
if (reason == u"mismatch" || reason == u"spoof") {
@@ -135,7 +141,7 @@ void BubbleController::failed(const QString &reason, qint64 lockout)
return;
}
setPhase(QStringLiteral("failure"));
m_hide.start(FailureHoldMs);
m_hide.start(int(FailureHoldMs * pace()));
}
void BubbleController::dismiss()
+4
View File
@@ -27,6 +27,8 @@ class BubbleController : public QObject
// Whether the window should be on screen. Stays true after the phase
// goes back to hidden until the bubble has finished closing.
Q_PROPERTY(bool shown READ shown NOTIFY shownChanged)
// The animation speed setting, as a multiple of each animation's length.
Q_PROPERTY(qreal pace READ pace NOTIFY paceChanged)
public:
explicit BubbleController(UserConfig *config, QObject *parent = nullptr);
@@ -47,6 +49,7 @@ public:
{
return m_shown;
}
qreal pace() const;
void scanStarted();
void faceFound();
@@ -68,6 +71,7 @@ Q_SIGNALS:
void faceSeenChanged();
void styleChanged();
void shownChanged();
void paceChanged();
private:
void setPhase(const QString &phase);
+1 -1
View File
@@ -189,7 +189,7 @@ void LockController::onScanFinished(const QJsonObject &result)
const QString reason = result.value(u"reason").toString();
if (result.value(u"ok").toBool()) {
m_bubble->succeeded();
QTimer::singleShot(UnlockAfterSuccessMs, this, &LockController::unlock);
QTimer::singleShot(int(UnlockAfterSuccessMs * m_config->pace()), this, &LockController::unlock);
return;
}
+26 -22
View File
@@ -26,6 +26,8 @@ Item {
readonly property string phase: bubble ? bubble.phase : "hidden"
readonly property bool minimal: bubble && bubble.style === "minimal"
readonly property bool wantOpen: phase !== "hidden"
// The animation speed setting: how long everything takes, 1 normal.
readonly property real pace: bubble ? bubble.pace : 1
property bool positioned: false
property bool expanded: false
@@ -54,12 +56,12 @@ Item {
Timer {
id: expandLater
interval: Theme.expandDelay
interval: Theme.expandDelay * root.pace
onTriggered: root.expanded = true
}
Timer {
id: slideOut
interval: Theme.slideOutDelay
interval: Theme.slideOutDelay * root.pace
onTriggered: {
root.positioned = false
closeDone.restart()
@@ -67,7 +69,7 @@ Item {
}
Timer {
id: closeDone
interval: Theme.slideOutDuration + 60
interval: Theme.slideOutDuration * root.pace + 60
onTriggered: if (root.bubble) root.bubble.closed()
}
@@ -85,11 +87,11 @@ Item {
id: pulseAnimation
running: root.phase === "scanning" && root.expanded
loops: Animation.Infinite
PauseAnimation { duration: 600 }
NumberAnimation { from: 0; to: 1; duration: 400; easing.type: Easing.InOutQuad }
PauseAnimation { duration: 50 }
NumberAnimation { from: 1; to: 0; duration: 400; easing.type: Easing.InOutQuad }
PauseAnimation { duration: 50 }
PauseAnimation { duration: 600 * root.pace }
NumberAnimation { from: 0; to: 1; duration: 400 * root.pace; easing.type: Easing.InOutQuad }
PauseAnimation { duration: 50 * root.pace }
NumberAnimation { from: 1; to: 0; duration: 400 * root.pace; easing.type: Easing.InOutQuad }
PauseAnimation { duration: 50 * root.pace }
onRunningChanged: if (!running) settle.restart()
}
NumberAnimation {
@@ -97,7 +99,7 @@ Item {
target: root
property: "pulse"
to: 0
duration: 200
duration: 200 * root.pace
easing.type: Easing.OutQuad
}
@@ -105,11 +107,11 @@ Item {
property real shake: 0
SequentialAnimation {
id: pillShake
NumberAnimation { target: root; property: "shake"; to: -10; duration: 55; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: 9; duration: 70 }
NumberAnimation { target: root; property: "shake"; to: -6; duration: 65 }
NumberAnimation { target: root; property: "shake"; to: 4; duration: 60 }
NumberAnimation { target: root; property: "shake"; to: 0; duration: 55; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: -10; duration: 55 * root.pace; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: 9; duration: 70 * root.pace }
NumberAnimation { target: root; property: "shake"; to: -6; duration: 65 * root.pace }
NumberAnimation { target: root; property: "shake"; to: 4; duration: 60 * root.pace }
NumberAnimation { target: root; property: "shake"; to: 0; duration: 55 * root.pace; easing.type: Easing.OutQuad }
}
Item {
@@ -126,14 +128,14 @@ Item {
// not sag.
Behavior on width {
NumberAnimation {
duration: root.opening ? Theme.growDuration : Theme.shrinkDuration
duration: (root.opening ? Theme.growDuration : Theme.shrinkDuration) * root.pace
easing.type: root.opening ? Easing.OutBack : Easing.OutCubic
easing.overshoot: 1.6
}
}
Behavior on height {
NumberAnimation {
duration: root.opening ? Theme.growDuration : Theme.shrinkDuration
duration: (root.opening ? Theme.growDuration : Theme.shrinkDuration) * root.pace
easing.type: root.opening ? Easing.OutBack : Easing.OutCubic
easing.overshoot: 0.9
}
@@ -145,7 +147,7 @@ Item {
y: root.positioned ? Theme.topGap : -Theme.closedHeight - 20
Behavior on y {
NumberAnimation {
duration: root.opening ? Theme.slideInDuration : Theme.slideOutDuration
duration: (root.opening ? Theme.slideInDuration : Theme.slideOutDuration) * root.pace
easing.type: root.opening ? Easing.OutCubic : Easing.InCubic
}
}
@@ -164,7 +166,7 @@ Item {
shadowOpacity: root.expanded ? 0.35 : 0
shadowBlur: 0.7
shadowVerticalOffset: 3
Behavior on shadowOpacity { NumberAnimation { duration: 200 } }
Behavior on shadowOpacity { NumberAnimation { duration: 200 * root.pace } }
}
}
@@ -178,7 +180,7 @@ Item {
visible: !root.minimal
scale: island.fit
opacity: root.expanded ? 1 : 0
Behavior on opacity { NumberAnimation { duration: root.opening ? 260 : 120 } }
Behavior on opacity { NumberAnimation { duration: (root.opening ? 260 : 120) * root.pace } }
readonly property bool hasMessage: root.bubble && root.bubble.message.length > 0
@@ -193,8 +195,9 @@ Item {
height: 100
anchors.horizontalCenter: parent.horizontalCenter
y: full.hasMessage ? 28 : 40
Behavior on y { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } }
Behavior on y { NumberAnimation { duration: 220 * root.pace; easing.type: Easing.OutCubic } }
mode: root.glyphMode
pace: root.pace
}
Text {
@@ -209,7 +212,7 @@ Item {
font.pixelSize: 13
font.weight: Font.Medium
opacity: full.hasMessage ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200 } }
Behavior on opacity { NumberAnimation { duration: 200 * root.pace } }
}
}
}
@@ -220,7 +223,7 @@ Item {
anchors.fill: parent
visible: root.minimal
opacity: root.expanded ? 1 : 0
Behavior on opacity { NumberAnimation { duration: root.opening ? 200 : 120 } }
Behavior on opacity { NumberAnimation { duration: (root.opening ? 200 : 120) * root.pace } }
LockGlyph {
width: 18
@@ -239,6 +242,7 @@ Item {
anchors.rightMargin: 14
lineWidth: 2.4
mode: root.glyphMode === "lockout" ? "failure" : root.glyphMode
pace: root.pace
onSettled: ok => {
if (!ok) {
pillShake.restart()
+22 -20
View File
@@ -26,6 +26,8 @@ Item {
property string mode: "idle"
property color color: Theme.textPrimary
property real lineWidth: width * 0.055
// How long everything takes, 1 normal (the animation speed setting).
property real pace: 1
// The verdict is showing: after the rings on success, at once on failure.
signal settled(bool success)
@@ -45,11 +47,11 @@ Item {
running: root.looking
from: 0
to: 2 * Math.PI
duration: 2000
duration: 2000 * root.pace
loops: Animation.Infinite
}
property real lookAmount: root.looking ? 1 : 0
Behavior on lookAmount { NumberAnimation { duration: 350; easing.type: Easing.InOutQuad } }
Behavior on lookAmount { NumberAnimation { duration: 350 * root.pace; easing.type: Easing.InOutQuad } }
readonly property real yaw: 0.34 * Math.cos(lookAngle) * lookAmount
readonly property real pitch: 0.2 * Math.sin(lookAngle) * lookAmount
@@ -65,15 +67,15 @@ Item {
// -- the smile, flat on failure
property real smile: mode === "failure" ? 0 : 1
Behavior on smile { NumberAnimation { duration: 220; easing.type: Easing.OutQuad } }
Behavior on smile { NumberAnimation { duration: 220 * root.pace; easing.type: Easing.OutQuad } }
// -- the brackets breathe while scanning and close in on a face
property real breathe: 0
SequentialAnimation on breathe {
running: root.looking
loops: Animation.Infinite
NumberAnimation { from: 0; to: 1; duration: 700; easing.type: Easing.InOutSine }
NumberAnimation { from: 1; to: 0; duration: 700; easing.type: Easing.InOutSine }
NumberAnimation { from: 0; to: 1; duration: 700 * root.pace; easing.type: Easing.InOutSine }
NumberAnimation { from: 1; to: 0; duration: 700 * root.pace; easing.type: Easing.InOutSine }
onRunningChanged: if (!running) root.breathe = 0
}
readonly property real bracketScale: mode === "tracking" ? 0.9 : 1 - 0.04 * breathe
@@ -126,11 +128,11 @@ Item {
SequentialAnimation {
id: succeed
ParallelAnimation {
NumberAnimation { target: root; property: "t"; from: 0; to: 1; duration: 850 }
NumberAnimation { target: root; property: "t"; from: 0; to: 1; duration: 850 * root.pace }
// The tick starts as the ring comes to rest, so the one motion
// runs on into the other.
SequentialAnimation {
PauseAnimation { duration: 470 }
PauseAnimation { duration: 470 * root.pace }
ScriptAction { script: drawTick.start() }
}
}
@@ -141,7 +143,7 @@ Item {
target: root
property: "tick"
to: 1
duration: 260
duration: 260 * root.pace
easing.type: Easing.OutQuad
}
@@ -222,12 +224,12 @@ Item {
property real shake: 0
SequentialAnimation {
id: shakeAnimation
NumberAnimation { target: root; property: "shake"; to: -9; duration: 55; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: 8; duration: 70; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: -6; duration: 65; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: 4; duration: 60; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: -2; duration: 55; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: 0; duration: 50; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: -9; duration: 55 * root.pace; easing.type: Easing.OutQuad }
NumberAnimation { target: root; property: "shake"; to: 8; duration: 70 * root.pace; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: -6; duration: 65 * root.pace; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: 4; duration: 60 * root.pace; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: -2; duration: 55 * root.pace; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "shake"; to: 0; duration: 50 * root.pace; easing.type: Easing.OutQuad }
}
Item {
@@ -246,7 +248,7 @@ Item {
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
scale: root.bracketScale
Behavior on scale { NumberAnimation { duration: 260; easing.type: Easing.OutBack } }
Behavior on scale { NumberAnimation { duration: 260 * root.pace; easing.type: Easing.OutBack } }
component Bracket: ShapePath {
strokeColor: root.bracketColor
@@ -254,7 +256,7 @@ Item {
fillColor: "transparent"
capStyle: ShapePath.RoundCap
joinStyle: ShapePath.RoundJoin
Behavior on strokeColor { ColorAnimation { duration: 220 } }
Behavior on strokeColor { ColorAnimation { duration: 220 * root.pace } }
}
Bracket {
@@ -352,7 +354,7 @@ Item {
preferredRendererType: Shape.CurveRenderer
// Gives way to the rings, and to the lock.
property real shown: root.mode === "lockout" ? 0 : 1
Behavior on shown { NumberAnimation { duration: 180 } }
Behavior on shown { NumberAnimation { duration: 180 * root.pace } }
opacity: shown * (1 - root.faceGone)
scale: 1 - 0.2 * root.faceGone
@@ -362,7 +364,7 @@ Item {
fillColor: "transparent"
capStyle: ShapePath.RoundCap
joinStyle: ShapePath.RoundJoin
Behavior on strokeColor { ColorAnimation { duration: 220 } }
Behavior on strokeColor { ColorAnimation { duration: 220 * root.pace } }
}
// Eyes
@@ -412,8 +414,8 @@ Item {
color: root.color
opacity: root.mode === "lockout" ? 1 : 0
scale: root.mode === "lockout" ? 1 : 0.7
Behavior on opacity { NumberAnimation { duration: 200 } }
Behavior on scale { NumberAnimation { duration: 260; easing.type: Easing.OutBack } }
Behavior on opacity { NumberAnimation { duration: 200 * root.pace } }
Behavior on scale { NumberAnimation { duration: 260 * root.pace; easing.type: Easing.OutBack } }
}
}
}
+2
View File
@@ -35,6 +35,8 @@ void UserConfig::load()
m_bubble = kv.boolean(QStringLiteral("Bubble"), true);
m_bubbleStyle = kv.value(QStringLiteral("BubbleStyle"), QStringLiteral("full")) == u"minimal" ? QStringLiteral("minimal") : QStringLiteral("full");
m_bubbleForPrompts = kv.boolean(QStringLiteral("BubbleForPrompts"), true);
const QString speed = kv.value(QStringLiteral("AnimationSpeed"), QStringLiteral("normal"));
m_pace = speed == u"fast" ? 0.6 : speed == u"slow" ? 1.5 : 1.0;
if (QFileInfo::exists(path()) && !m_watcher.files().contains(path())) {
m_watcher.addPath(path());
+8
View File
@@ -13,6 +13,7 @@ class UserConfig : public QObject
Q_OBJECT
Q_PROPERTY(bool bubble READ bubble NOTIFY changed)
Q_PROPERTY(QString bubbleStyle READ bubbleStyle NOTIFY changed)
Q_PROPERTY(qreal pace READ pace NOTIFY changed)
public:
explicit UserConfig(QObject *parent = nullptr);
@@ -54,6 +55,12 @@ public:
{
return m_bubbleForPrompts;
}
// How long the bubble's animations take, as a multiple of normal: the
// animation speed setting (fast 0.6, normal 1, slow 1.5).
qreal pace() const
{
return m_pace;
}
static QString path();
@@ -70,5 +77,6 @@ private:
bool m_bubble = true;
QString m_bubbleStyle = QStringLiteral("full");
bool m_bubbleForPrompts = true;
qreal m_pace = 1;
QString m_styleOverride;
};
+4
View File
@@ -182,6 +182,9 @@ pfu_value_label() {
Strictness:relaxed) pfu_msg "relaxed" ;;
BubbleStyle:full) pfu_msg "island with the face" ;;
BubbleStyle:minimal) pfu_msg "small pill with a lock" ;;
AnimationSpeed:normal) pfu_msg "normal" ;;
AnimationSpeed:fast) pfu_msg "fast" ;;
AnimationSpeed:slow) pfu_msg "slow" ;;
ScanSeconds:*) pfu_msg "%s seconds" "$2" ;;
Camera:auto) pfu_msg "automatic" ;;
*) printf '%s' "$2" ;;
@@ -266,6 +269,7 @@ PFU_SETTINGS=(
"sys|SkipLidClosed|bool|yes|Not while the lid is closed"
"user|Bubble|bool|yes|Show the bubble at the top"
"user|BubbleStyle|choice|full|Bubble style|full,minimal"
"user|AnimationSpeed|choice|normal|Animation speed|normal,fast,slow"
"user|BubbleForPrompts|bool|yes|Bubble for sudo and admin prompts too"
)