From 6688c4a5137979d379f8be59178801dfeab9491e Mon Sep 17 00:00:00 2001 From: ivan tkachenko Date: Fri, 6 May 2022 21:12:55 +0300 Subject: [PATCH] [lookandfeel,sddm-theme] Add rejection animation in response to a wrong password This is a stripped down version of customizable demo available at my invent repo[1]. It is optimized for this particular use-case. Animation honors right-to-left application layout and "instant animation speed" preference. [1]: https://invent.kde.org/ratijas/reject-input-animation/ --- .../animation/RejectPasswordAnimation.qml | 42 +++++++++++++++ .../animation/RejectPasswordPathAnimation.qml | 53 +++++++++++++++++++ .../contents/lockscreen/LockScreenUi.qml | 7 +++ sddm-theme/Main.qml | 7 +++ 4 files changed, 109 insertions(+) create mode 100644 lookandfeel/contents/components/animation/RejectPasswordAnimation.qml create mode 100644 lookandfeel/contents/components/animation/RejectPasswordPathAnimation.qml diff --git a/lookandfeel/contents/components/animation/RejectPasswordAnimation.qml b/lookandfeel/contents/components/animation/RejectPasswordAnimation.qml new file mode 100644 index 000000000..645dcd36c --- /dev/null +++ b/lookandfeel/contents/components/animation/RejectPasswordAnimation.qml @@ -0,0 +1,42 @@ +/* + SPDX-FileCopyrightText: 2022 ivan (@ratijas) tkachenko + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +import QtQuick 2.15 +import QtQml 2.15 + +QtObject { + id: root + + property Item target + + readonly property Animation __animation: RejectPasswordPathAnimation { + id: animation + target: Item { id: fakeTarget } + } + + property Binding __bindEnabled: Binding { + target: root.target + property: "enabled" + value: false + when: animation.running + restoreMode: Binding.RestoreBindingOrValue + } + + // real target is getting a Translate object which pulls coordinates from + // a fake Item object + property Binding __bindTransform: Binding { + target: root.target + property: "transform" + value: Translate { + x: fakeTarget.x + } + restoreMode: Binding.RestoreBindingOrValue + } + + function start() { + animation.start(); + } +} diff --git a/lookandfeel/contents/components/animation/RejectPasswordPathAnimation.qml b/lookandfeel/contents/components/animation/RejectPasswordPathAnimation.qml new file mode 100644 index 000000000..bfddd9ff3 --- /dev/null +++ b/lookandfeel/contents/components/animation/RejectPasswordPathAnimation.qml @@ -0,0 +1,53 @@ +/* + SPDX-FileCopyrightText: 2022 ivan (@ratijas) tkachenko + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +import QtQuick 2.15 +import QtQml 2.15 + +import org.kde.plasma.core 2.0 as PlasmaCore + +PathAnimation { + id: root + + /** The magnitude/distance/offset of the animation, in the usual device-independent pixels. */ + property real swing: 15 + + /** + * In which direction the target starts moving first. + * Must be either Qt.LeftToRight or Qt.RightToLeft. + * + * By default it is opposite to the application's layout direction, to + * make an animation feel more "disturbing". + */ + property int initialDirection: Qt.application.layoutDirection === Qt.RightToLeft ? Qt.LeftToRight : Qt.RightToLeft + + alwaysRunToEnd: true + + // This animation's speed does not depend on user preferences, except when + // we honor the "reduced animations" special case. + // Animators with a duration of 0 do not fire reliably, which is why duration is at least 1. + // see Bug 357532 and QTBUG-39766 + duration: PlasmaCore.Units.longDuration <= 1 ? 1 : 600 + easing.type: Easing.OutCubic + + path: Path { + PathPolyline { + path: { + const directionFactor = root.initialDirection === Qt.RightToLeft ? -1 : 1; + const extreme = root.swing * directionFactor; + const here = Qt.point(extreme, 0); + const there = Qt.point(-extreme, 0); + return [ + Qt.point(0, 0), + here, there, + here, there, + here, there, + Qt.point(0, 0), + ]; + } + } + } +} diff --git a/lookandfeel/contents/lockscreen/LockScreenUi.qml b/lookandfeel/contents/lockscreen/LockScreenUi.qml index ee9c448b2..05e1a4f45 100644 --- a/lookandfeel/contents/lockscreen/LockScreenUi.qml +++ b/lookandfeel/contents/lockscreen/LockScreenUi.qml @@ -15,6 +15,7 @@ import org.kde.plasma.workspace.components 2.0 as PW import org.kde.plasma.private.sessions 2.0 import "../components" +import "../components/animation" PlasmaCore.ColorScope { @@ -34,6 +35,7 @@ PlasmaCore.ColorScope { root.notification += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Unlocking failed"); graceLockTimer.restart(); notificationRemoveTimer.restart(); + rejectPasswordAnimation.start(); } function onSucceeded() { @@ -95,6 +97,11 @@ PlasmaCore.ColorScope { visible: false } + RejectPasswordAnimation { + id: rejectPasswordAnimation + target: mainBlock + } + MouseArea { id: lockScreenRoot diff --git a/sddm-theme/Main.qml b/sddm-theme/Main.qml index 38a1170a6..ef6ecc1b7 100644 --- a/sddm-theme/Main.qml +++ b/sddm-theme/Main.qml @@ -14,6 +14,7 @@ import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import "components" +import "components/animation" // TODO: Once SDDM 0.19 is released and we are setting the font size using the // SDDM KCM's syncing feature, remove the `config.fontSize` overrides here and @@ -58,6 +59,11 @@ PlasmaCore.ColorScope { } } + RejectPasswordAnimation { + id: rejectPasswordAnimation + target: mainStack + } + MouseArea { id: loginScreenRoot anchors.fill: parent @@ -587,6 +593,7 @@ PlasmaCore.ColorScope { footer.enabled = true mainStack.enabled = true userListComponent.userList.opacity = 1 + rejectPasswordAnimation.start() } function onLoginSucceeded() { //note SDDM will kill the greeter at some random point after this