diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2025-09-04 20:14:56 +0200 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2025-09-04 20:14:56 +0200 |
| commit | 862ddc9059883900024d3e41f6a86f259134035a (patch) | |
| tree | e2036ea19483b3e38e4bbd31d1ef532af0cf89c8 /accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | |
| parent | cc84ab2289381038f483f06963374aa0247f6724 (diff) | |
| download | nixos-862ddc9059883900024d3e41f6a86f259134035a.tar nixos-862ddc9059883900024d3e41f6a86f259134035a.tar.gz nixos-862ddc9059883900024d3e41f6a86f259134035a.tar.bz2 nixos-862ddc9059883900024d3e41f6a86f259134035a.tar.xz nixos-862ddc9059883900024d3e41f6a86f259134035a.zip | |
...
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/Lockscreen.qml')
| -rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml new file mode 100644 index 00000000..c7d28ffa --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | |||
| @@ -0,0 +1,260 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import Quickshell.Io | ||
| 4 | import Quickshell.Services.Pam | ||
| 5 | import Quickshell.Widgets | ||
| 6 | import QtQuick.Effects | ||
| 7 | import QtQuick.Layouts | ||
| 8 | import QtQuick | ||
| 9 | import QtQuick.Controls | ||
| 10 | import QtQuick.Controls.Fusion | ||
| 11 | import qs.Services | ||
| 12 | // import QtQml.Models | ||
| 13 | |||
| 14 | Scope { | ||
| 15 | id: lockscreen | ||
| 16 | |||
| 17 | property string currentText: "" | ||
| 18 | |||
| 19 | PamContext { | ||
| 20 | id: pam | ||
| 21 | |||
| 22 | property list<var> messages: [] | ||
| 23 | |||
| 24 | config: "quickshell" | ||
| 25 | onCompleted: result => { | ||
| 26 | if (result === PamResult.Success) { | ||
| 27 | lock.locked = false; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | onPamMessage: { | ||
| 31 | messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }]) | ||
| 32 | } | ||
| 33 | onActiveChanged: { | ||
| 34 | messages = []; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | IpcHandler { | ||
| 39 | target: "lock" | ||
| 40 | |||
| 41 | function lock(): void { lock.locked = true; } | ||
| 42 | } | ||
| 43 | |||
| 44 | WlSessionLock { | ||
| 45 | id: lock | ||
| 46 | |||
| 47 | onLockedChanged: { | ||
| 48 | if (!locked && pam.active) | ||
| 49 | pam.abort(); | ||
| 50 | } | ||
| 51 | |||
| 52 | WlSessionLockSurface { | ||
| 53 | id: lockSurface | ||
| 54 | |||
| 55 | color: "#000000" | ||
| 56 | |||
| 57 | onScreenChanged: selector.seed = lockSurface.screen.name | ||
| 58 | |||
| 59 | Item { | ||
| 60 | id: background | ||
| 61 | |||
| 62 | anchors.fill: parent | ||
| 63 | |||
| 64 | property Img current: one | ||
| 65 | property string source: selector.selected | ||
| 66 | |||
| 67 | WallpaperSelector { | ||
| 68 | id: selector | ||
| 69 | seed: "" | ||
| 70 | } | ||
| 71 | |||
| 72 | onSourceChanged: { | ||
| 73 | if (!source) | ||
| 74 | current = null; | ||
| 75 | else if (current === one) | ||
| 76 | two.update() | ||
| 77 | else | ||
| 78 | one.update() | ||
| 79 | } | ||
| 80 | |||
| 81 | Img { id: one } | ||
| 82 | Img { id: two } | ||
| 83 | |||
| 84 | component Img: Item { | ||
| 85 | id: img | ||
| 86 | |||
| 87 | property string source | ||
| 88 | |||
| 89 | function update() { | ||
| 90 | source = background.source || "" | ||
| 91 | } | ||
| 92 | |||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Image { | ||
| 96 | id: imageSource | ||
| 97 | |||
| 98 | source: img.source | ||
| 99 | sourceSize: Qt.size(parent.width, parent.height) | ||
| 100 | fillMode: Image.PreserveAspectCrop | ||
| 101 | smooth: true | ||
| 102 | visible: false | ||
| 103 | asynchronous: true | ||
| 104 | cache: false | ||
| 105 | |||
| 106 | onStatusChanged: { | ||
| 107 | if (status === Image.Ready) { | ||
| 108 | background.current = img | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | MultiEffect { | ||
| 114 | id: imageEffect | ||
| 115 | |||
| 116 | source: imageSource | ||
| 117 | anchors.fill: parent | ||
| 118 | blurEnabled: true | ||
| 119 | blur: 1 | ||
| 120 | blurMax: 64 | ||
| 121 | blurMultiplier: 2 | ||
| 122 | |||
| 123 | opacity: 0 | ||
| 124 | |||
| 125 | states: State { | ||
| 126 | name: "visible" | ||
| 127 | when: background.current === img | ||
| 128 | |||
| 129 | PropertyChanges { | ||
| 130 | imageEffect.opacity: 1 | ||
| 131 | } | ||
| 132 | StateChangeScript { | ||
| 133 | name: "unloadOther" | ||
| 134 | script: { | ||
| 135 | if (img === one) | ||
| 136 | two.source = "" | ||
| 137 | if (img === two) | ||
| 138 | one.source = "" | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | transitions: Transition { | ||
| 144 | SequentialAnimation { | ||
| 145 | NumberAnimation { | ||
| 146 | target: imageEffect | ||
| 147 | properties: "opacity" | ||
| 148 | duration: 5000 | ||
| 149 | easing.type: Easing.OutCubic | ||
| 150 | } | ||
| 151 | ScriptAction { | ||
| 152 | scriptName: "unloadOther" | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | Item { | ||
| 161 | anchors { | ||
| 162 | top: lockSurface.top | ||
| 163 | left: lockSurface.left | ||
| 164 | right: lockSurface.right | ||
| 165 | } | ||
| 166 | |||
| 167 | implicitWidth: lockSurface.width | ||
| 168 | implicitHeight: 21 | ||
| 169 | |||
| 170 | Rectangle { | ||
| 171 | anchors.fill: parent | ||
| 172 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 173 | } | ||
| 174 | |||
| 175 | Clock { | ||
| 176 | anchors.centerIn: parent | ||
| 177 | calendarPopup: false | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | WrapperRectangle { | ||
| 182 | id: unlockUi | ||
| 183 | |||
| 184 | Keys.onPressed: event => { | ||
| 185 | if (!pam.active) { | ||
| 186 | event.accepted = true; | ||
| 187 | pam.start(); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | focus: !passwordBox.visible | ||
| 191 | |||
| 192 | visible: pam.active | ||
| 193 | |||
| 194 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 195 | margin: 8 | ||
| 196 | |||
| 197 | anchors.centerIn: parent | ||
| 198 | |||
| 199 | ColumnLayout { | ||
| 200 | spacing: 4 | ||
| 201 | |||
| 202 | BusyIndicator { | ||
| 203 | visible: running | ||
| 204 | running: !Array.from(pam.messages).length && !pam.responseRequired | ||
| 205 | } | ||
| 206 | |||
| 207 | Repeater { | ||
| 208 | model: pam.messages | ||
| 209 | |||
| 210 | Text { | ||
| 211 | required property var modelData | ||
| 212 | |||
| 213 | font.pointSize: 10 | ||
| 214 | font.family: "Fira Sans" | ||
| 215 | color: modelData.error ? "#f28a21" : "#ffffff" | ||
| 216 | |||
| 217 | text: modelData.text | ||
| 218 | |||
| 219 | Layout.fillWidth: true | ||
| 220 | horizontalAlignment: Text.AlignHCenter | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | TextField { | ||
| 225 | id: passwordBox | ||
| 226 | |||
| 227 | visible: pam.responseRequired | ||
| 228 | echoMode: pam.responseVisible ? TextInput.Normal : TextInput.Password | ||
| 229 | inputMethodHints: Qt.ImhSensitiveData | ||
| 230 | |||
| 231 | onTextChanged: lockscreen.currentText = passwordBox.text | ||
| 232 | onAccepted: { | ||
| 233 | passwordBox.readOnly = true; | ||
| 234 | pam.respond(lockscreen.currentText); | ||
| 235 | } | ||
| 236 | |||
| 237 | Connections { | ||
| 238 | target: lockscreen | ||
| 239 | function onCurrentTextChanged() { | ||
| 240 | passwordBox.text = lockscreen.currentText | ||
| 241 | } | ||
| 242 | } | ||
| 243 | Connections { | ||
| 244 | target: pam | ||
| 245 | function onResponseRequiredChanged() { | ||
| 246 | if (pam.responseRequired) | ||
| 247 | passwordBox.readOnly = false; | ||
| 248 | passwordBox.focus = true; | ||
| 249 | passwordBox.selectAll(); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | Layout.topMargin: 4 | ||
| 254 | Layout.fillWidth: true | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
