import QtQuick 2.15 import QtQuick.Controls 2.15 import org.kde.plasma.core 2.0 as PlasmaCore /* * NexusOS kscreenlocker theme. * Mirrors the SDDM NexusOS-QML aesthetic: logo upper-center, * clock/date bottom-center, centered password box. * * kscreenlocker API differences from SDDM: * - authenticator.tryUnlock(password) instead of sddm.login(...) * - authenticator.failed / authenticator.succeeded signals * - No session selector, no power buttons * - walletModel, userModel available but not mandatory */ Rectangle { id: root // kscreenlocker injects these from the surrounding PlasmaShell context property bool locked: true readonly property color accentColor: "#8cc63f" // brand_green readonly property color bgDark: "#1e1526" // base_bg readonly property color textPrimary: "#f2f2f2" readonly property color textMuted: "#a8a8a8" readonly property color fieldBg: "#1f2225" // surface_bg readonly property color fieldBorder: "#4d3461" // menu_border readonly property color errorColor: "#da4453" anchors.fill: parent color: bgDark // ── Background image (reuses SDDM background asset) ──────────────── Image { anchors.fill: parent source: Qt.resolvedUrl("../../assets/background.svg") fillMode: Image.PreserveAspectCrop smooth: true asynchronous: false } Rectangle { anchors.fill: parent color: "#000000" opacity: 0.30 } // ── Logo area ─────────────────────────────────────────────────────── Item { id: logoArea width: 100 height: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: parent.height * 0.10 Rectangle { anchors.centerIn: parent width: parent.width * 1.6 height: parent.height * 1.6 radius: width / 2 color: accentColor opacity: 0.06 } Image { anchors.fill: parent source: Qt.resolvedUrl("../../assets/logo.png") sourceSize: Qt.size(200, 200) smooth: true } } Text { text: "NexusOS" color: textPrimary font.family: "Sans" font.pixelSize: 26 font.letterSpacing: 6 font.weight: Font.Light anchors.horizontalCenter: parent.horizontalCenter anchors.top: logoArea.bottom anchors.topMargin: 14 } // ── Password box ──────────────────────────────────────────────────── Rectangle { id: loginBox width: 340 height: loginColumn.height + 56 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter color: Qt.rgba(30/255, 21/255, 38/255, 0.80) radius: 12 border.color: fieldBorder border.width: 1 SequentialAnimation { id: shakeAnim NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 10; duration: 50 } NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 10; duration: 50 } NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 6; duration: 50 } NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 6; duration: 50 } NumberAnimation { target: loginBox; property: "x"; to: loginBox.x; duration: 40 } } Column { id: loginColumn anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: 28 spacing: 12 // Username label (read-only; kscreenlocker always locks current session) Text { width: parent.width text: kscreenlocker_userName || userModel.data(userModel.index(0, 0), Qt.DisplayRole) || "" color: textPrimary font.pixelSize: 14 font.weight: Font.Medium horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } // Password field Rectangle { width: parent.width height: 44 color: fieldBg radius: 6 border.color: passwordInput.activeFocus ? accentColor : fieldBorder border.width: 1 TextInput { id: passwordInput anchors.fill: parent anchors.leftMargin: 14 anchors.rightMargin: 14 verticalAlignment: TextInput.AlignVCenter color: textPrimary font.pixelSize: 14 echoMode: TextInput.Password focus: true clip: true Keys.onReturnPressed: authenticator.tryUnlock(passwordInput.text) Keys.onEnterPressed: authenticator.tryUnlock(passwordInput.text) } Text { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 14 text: "Password" color: textMuted font.pixelSize: 14 visible: passwordInput.text.length === 0 && !passwordInput.activeFocus } } // Error message Text { id: errorMsg width: parent.width text: "" color: errorColor font.pixelSize: 12 horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap visible: text !== "" } // Unlock button Rectangle { id: unlockButton width: parent.width height: 44 radius: 6 color: unlockMouse.pressed ? Qt.darker(accentColor, 1.3) : unlockMouse.containsMouse ? Qt.lighter(accentColor, 1.15) : accentColor Behavior on color { ColorAnimation { duration: 150 } } Text { anchors.centerIn: parent text: "UNLOCK" color: "#0a0a00" // text_on_accent font.pixelSize: 14 font.letterSpacing: 3 font.weight: Font.DemiBold } MouseArea { id: unlockMouse anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: authenticator.tryUnlock(passwordInput.text) } } } } // ── Authenticator connections ─────────────────────────────────────── Connections { target: authenticator function onFailed() { errorMsg.text = "Incorrect password — try again." passwordInput.text = "" passwordInput.forceActiveFocus() shakeAnim.start() } function onSucceeded() { errorMsg.text = "" } function onGraceLockedChanged() {} function onMessage(msg) {} function onError(err) { errorMsg.text = err } } // ── Clock / date (bottom) ─────────────────────────────────────────── Column { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: parent.height * 0.05 spacing: 2 Text { id: clockText anchors.horizontalCenter: parent.horizontalCenter color: textPrimary font.family: "Sans" font.pixelSize: 42 font.weight: Font.Thin } Text { id: dateText anchors.horizontalCenter: parent.horizontalCenter color: textMuted font.family: "Sans" font.pixelSize: 14 font.letterSpacing: 2 } Timer { interval: 1000 running: true repeat: true triggeredOnStart: true onTriggered: { var d = new Date() clockText.text = Qt.formatTime(d, "hh:mm") dateText.text = Qt.formatDate(d, "dddd, MMMM d") } } } }