import Quickshell import Quickshell.Wayland import Quickshell.Io import Quickshell.Services.Pam import Quickshell.Widgets import QtQuick.Effects import QtQuick.Layouts import QtQuick import QtQuick.Controls import QtQuick.Controls.Fusion import qs.Services // import QtQml.Models Scope { id: lockscreen property string currentText: "" PamContext { id: pam property list messages: [] config: "quickshell" onCompleted: result => { if (result === PamResult.Success) { lock.locked = false; } } onPamMessage: { messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }]) } onActiveChanged: { messages = []; } } IpcHandler { target: "Lockscreen" function setLocked(locked: bool): void { lock.locked = locked; } function getLocked(): bool { return lock.locked; } } WlSessionLock { id: lock onLockedChanged: { if (!locked && pam.active) pam.abort(); } WlSessionLockSurface { id: lockSurface color: "#000000" Item { id: background anchors.fill: parent property Img current: one property string source: selector.selected WallpaperSelector { id: selector seed: lockSurface.screen?.name || "" } onSourceChanged: { if (!source) current = null; else if (current === one) two.update() else one.update() } Img { id: one } Img { id: two } component Img: Item { id: img property string source function update() { source = background.source || "" } anchors.fill: parent Image { id: imageSource source: img.source sourceSize: Qt.size(parent.width, parent.height) fillMode: Image.PreserveAspectCrop smooth: true visible: false asynchronous: true cache: false onStatusChanged: { if (status === Image.Ready) { background.current = img } } } MultiEffect { id: imageEffect source: imageSource anchors.fill: parent blurEnabled: true blur: 1 blurMax: 64 blurMultiplier: 2 opacity: 0 states: State { name: "visible" when: background.current === img PropertyChanges { imageEffect.opacity: 1 } StateChangeScript { name: "unloadOther" script: { if (img === one) two.source = "" if (img === two) one.source = "" } } } transitions: Transition { SequentialAnimation { NumberAnimation { target: imageEffect properties: "opacity" duration: 5000 easing.type: Easing.OutCubic } ScriptAction { scriptName: "unloadOther" } } } } } } Item { anchors { top: lockSurface.top left: lockSurface.left right: lockSurface.right } implicitWidth: lockSurface.width implicitHeight: 21 Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.75) } Clock { anchors.centerIn: parent calendarPopup: false } } WrapperRectangle { id: unlockUi Keys.onPressed: event => { if (!pam.active) { event.accepted = true; pam.start(); } } focus: !passwordBox.visible visible: pam.active color: Qt.rgba(0, 0, 0, 0.75) margin: 8 anchors.centerIn: parent ColumnLayout { spacing: 4 BusyIndicator { visible: running running: !Array.from(pam.messages).length && !pam.responseRequired } Repeater { model: pam.messages Text { required property var modelData font.pointSize: 10 font.family: "Fira Sans" color: modelData.error ? "#f28a21" : "#ffffff" text: modelData.text Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } } TextField { id: passwordBox visible: pam.responseRequired echoMode: pam.responseVisible ? TextInput.Normal : TextInput.Password inputMethodHints: Qt.ImhSensitiveData onTextChanged: lockscreen.currentText = passwordBox.text onAccepted: { passwordBox.readOnly = true; pam.respond(lockscreen.currentText); } Connections { target: lockscreen function onCurrentTextChanged() { passwordBox.text = lockscreen.currentText } } Connections { target: pam function onResponseRequiredChanged() { if (pam.responseRequired) passwordBox.readOnly = false; passwordBox.focus = true; passwordBox.selectAll(); } } Layout.topMargin: 4 Layout.fillWidth: true } } } } } }