Match the login screen to the lock screen

This commit is contained in:
SirBlob committed 2026-09-27 01:16:20 +00:00
1 parent 8da99c25e9
commit 6a49d5e0cb
1 file changed
+173 -214
+173 -214
View File
@@ -8,96 +8,38 @@ Rectangle {
readonly property color errorColor: "#f7768e" readonly property color errorColor: "#f7768e"
readonly property string fontFamily: "monospace" readonly property string fontFamily: "monospace"
readonly property color mutedTextColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 0.55) readonly property color placeholderColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 0.66)
readonly property color faintTextColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 0.18) readonly property color restingBorderColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 0.5)
readonly property color panelColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 0.04) readonly property color fieldBackgroundColor: Qt.darker(backgroundColor, 1.25)
readonly property int fieldWidth: 381
readonly property int fieldHeight: 67
readonly property int outlineThickness: 2
readonly property int fieldFontSize: 18
readonly property int passwordDotFontSize: 21
readonly property int passwordDotLetterSpacing: 3
readonly property int clockFontSize: 72
readonly property int dateFontSize: 18
readonly property int smallFontSize: 14
readonly property int sectionGap: 48
readonly property int edgeMargin: 32
property string username: userModel.lastUser
property bool enteringUsername: username.length === 0
property int sessionIndex: sessionModel.lastIndex >= 0 ? sessionModel.lastIndex : 0 property int sessionIndex: sessionModel.lastIndex >= 0 ? sessionModel.lastIndex : 0
property string statusMessage: "" property string failureMessage: ""
property bool loggingIn: false property bool authenticating: false
readonly property bool errorState: failureMessage.length > 0
readonly property bool inputActive: fieldInput.text.length > 0 || authenticating
readonly property string placeholderText: authenticating ? "Checking..."
: errorState ? failureMessage
: enteringUsername ? "Enter Username"
: "Enter Password"
width: 1920 width: 1920
height: 1080 height: 1080
color: backgroundColor
gradient: Gradient {
GradientStop { position: 0.0; color: root.backgroundColor }
GradientStop { position: 1.0; color: Qt.darker(root.backgroundColor, 1.35) }
}
component LoginField: Item {
id: field
property string label: ""
property bool hasError: false
property bool isPassword: false
property Item nextField: null
property Item previousField: null
property alias input: fieldInput
property alias text: fieldInput.text
signal submitted()
width: 320
height: 58
Text {
id: fieldLabel
text: field.label.toUpperCase()
color: fieldInput.activeFocus ? root.textColor : root.mutedTextColor
font.family: root.fontFamily
font.pixelSize: 11
font.letterSpacing: 2
}
TextInput {
id: fieldInput
anchors.left: parent.left
anchors.right: parent.right
anchors.top: fieldLabel.bottom
anchors.topMargin: 10
color: root.textColor
selectionColor: root.faintTextColor
echoMode: field.isPassword ? TextInput.Password : TextInput.Normal
passwordCharacter: "•"
font.family: root.fontFamily
font.pixelSize: 18
clip: true
KeyNavigation.tab: field.nextField ? field.nextField.input : null
KeyNavigation.backtab: field.previousField ? field.previousField.input : null
Keys.onReturnPressed: field.submitted()
Keys.onEnterPressed: field.submitted()
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: fieldInput.activeFocus || field.hasError ? 2 : 1
color: field.hasError ? root.errorColor
: fieldInput.activeFocus ? root.textColor
: root.faintTextColor
}
}
component FooterButton: Text {
id: footerButton
signal clicked()
color: footerArea.containsMouse ? root.textColor : root.mutedTextColor
font.family: root.fontFamily
font.pixelSize: 12
font.letterSpacing: 2
MouseArea {
id: footerArea
anchors.fill: parent
anchors.margins: -8
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: footerButton.clicked()
}
}
function sessionName(index) { function sessionName(index) {
var session = sessionNames.itemAt(index) var session = sessionNames.itemAt(index)
@@ -108,15 +50,28 @@ Rectangle {
if (sessionNames.count > 0) root.sessionIndex = (root.sessionIndex + 1) % sessionNames.count if (sessionNames.count > 0) root.sessionIndex = (root.sessionIndex + 1) % sessionNames.count
} }
function login() { function changeUser() {
if (root.loggingIn) return root.failureMessage = ""
if (usernameField.text.length === 0) { root.enteringUsername = true
usernameField.input.forceActiveFocus() fieldInput.text = root.username
fieldInput.forceActiveFocus()
}
function submit() {
var entered = fieldInput.text
if (root.authenticating || entered.length === 0) return
if (root.enteringUsername) {
root.username = entered
root.enteringUsername = false
fieldInput.text = ""
return return
} }
root.loggingIn = true
root.statusMessage = "" fieldInput.text = ""
sddm.login(usernameField.text, passwordField.text, root.sessionIndex) root.failureMessage = ""
root.authenticating = true
sddm.login(root.username, entered, root.sessionIndex)
} }
Repeater { Repeater {
@@ -133,14 +88,9 @@ Rectangle {
target: sddm target: sddm
function onLoginFailed() { function onLoginFailed() {
root.loggingIn = false root.authenticating = false
root.statusMessage = "Wrong password, try again" root.failureMessage = "Wrong password"
passwordField.text = "" fieldInput.forceActiveFocus()
passwordField.input.forceActiveFocus()
}
function onLoginSucceeded() {
root.statusMessage = ""
} }
} }
@@ -156,166 +106,175 @@ Rectangle {
onTriggered: clock.now = new Date() onTriggered: clock.now = new Date()
} }
Column { MouseArea {
anchors.centerIn: parent anchors.fill: parent
spacing: 48 onClicked: fieldInput.forceActiveFocus()
}
Image {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: clockColumn.top
anchors.bottomMargin: root.sectionGap
width: 96
height: 96
source: "logo.png"
sourceSize.width: 192
sourceSize.height: 192
fillMode: Image.PreserveAspectFit
onStatusChanged: if (status === Image.Error && source.toString().endsWith("logo.png")) source = "logo.svg"
}
Column { Column {
id: clockColumn
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
spacing: 4 anchors.bottom: inputField.top
anchors.bottomMargin: root.sectionGap
Text { Text {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Qt.formatTime(clock.now, "HH:mm") text: Qt.formatTime(clock.now, "HH:mm")
color: root.textColor color: root.textColor
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 104 font.pixelSize: root.clockFontSize
font.weight: Font.Light
} }
Text { Text {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Qt.formatDate(clock.now, "dddd, MMMM d").toUpperCase() text: Qt.formatDate(clock.now, "dddd, d MMMM")
color: root.mutedTextColor color: root.placeholderColor
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 14 font.pixelSize: root.dateFontSize
font.letterSpacing: 4
} }
} }
Rectangle { Rectangle {
anchors.horizontalCenter: parent.horizontalCenter id: inputField
width: 400
height: loginColumn.implicitHeight + 80
color: root.panelColor
border.width: 1
border.color: root.faintTextColor
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 2
color: root.statusMessage.length > 0 ? root.errorColor : root.textColor
}
Column {
id: loginColumn
anchors.centerIn: parent anchors.centerIn: parent
spacing: 28 width: root.fieldWidth
height: root.fieldHeight
color: root.fieldBackgroundColor
border.width: root.outlineThickness
border.color: root.errorState ? root.errorColor
: root.inputActive ? root.textColor
: root.restingBorderColor
Row { TextInput {
spacing: 16 id: fieldInput
anchors.fill: parent
Image { anchors.margins: root.outlineThickness
id: logo anchors.leftMargin: 18
width: 40 anchors.rightMargin: 18
height: 40 verticalAlignment: TextInput.AlignVCenter
source: "logo.png" horizontalAlignment: TextInput.AlignHCenter
sourceSize.width: 80 clip: true
sourceSize.height: 80 focus: true
fillMode: Image.PreserveAspectFit readOnly: root.authenticating
anchors.verticalCenter: parent.verticalCenter echoMode: root.enteringUsername ? TextInput.Normal : TextInput.Password
onStatusChanged: if (status === Image.Error && source.toString().endsWith("logo.png")) source = "logo.svg" passwordCharacter: "●"
} passwordMaskDelay: 0
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 4
Text {
text: "Welcome back"
color: root.textColor color: root.textColor
selectionColor: root.restingBorderColor
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 18 font.pixelSize: text.length > 0 && !root.enteringUsername ? root.passwordDotFontSize : root.fieldFontSize
font.letterSpacing: text.length > 0 && !root.enteringUsername ? root.passwordDotLetterSpacing : 0
cursorDelegate: Rectangle {
width: 2
color: root.textColor
visible: fieldInput.cursorVisible && fieldInput.text.length > 0
}
onTextChanged: if (text.length > 0) root.failureMessage = ""
onAccepted: root.submit()
Keys.onEscapePressed: text = ""
} }
Text { Text {
text: "Sign in to continue" anchors.fill: fieldInput
color: root.mutedTextColor visible: fieldInput.text.length === 0
text: root.placeholderText
color: root.authenticating ? root.textColor
: root.errorState ? root.errorColor
: root.placeholderColor
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 12 font.pixelSize: root.fieldFontSize
font.italic: root.errorState
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
} }
} }
}
LoginField {
id: usernameField
label: "Username"
text: userModel.lastUser
nextField: passwordField
onSubmitted: passwordField.input.forceActiveFocus()
}
LoginField {
id: passwordField
label: "Password"
hasError: root.statusMessage.length > 0
isPassword: true
previousField: usernameField
onSubmitted: root.login()
}
Text { Text {
width: 320 anchors.horizontalCenter: parent.horizontalCenter
height: 16 anchors.top: inputField.bottom
text: root.statusMessage.length > 0 ? root.statusMessage anchors.topMargin: 20
: root.loggingIn ? "Signing in..." visible: !root.enteringUsername
: keyboard.capsLock ? "Caps Lock is on" text: root.username
: "Press Enter to sign in" color: userArea.containsMouse ? root.textColor : root.placeholderColor
color: root.statusMessage.length > 0 ? root.errorColor : root.mutedTextColor
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 12 font.pixelSize: root.smallFontSize
}
} MouseArea {
id: userArea
anchors.fill: parent
anchors.margins: -8
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.changeUser()
} }
} }
Rectangle { Text {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
height: 64 anchors.margins: root.edgeMargin
color: root.panelColor text: root.sessionName(root.sessionIndex)
color: sessionArea.containsMouse ? root.textColor : root.placeholderColor
font.family: root.fontFamily
font.pixelSize: root.smallFontSize
Rectangle { MouseArea {
anchors.top: parent.top id: sessionArea
anchors.left: parent.left anchors.fill: parent
anchors.right: parent.right anchors.margins: -8
height: 1 hoverEnabled: true
color: root.faintTextColor cursorShape: Qt.PointingHandCursor
}
FooterButton {
anchors.left: parent.left
anchors.leftMargin: 32
anchors.verticalCenter: parent.verticalCenter
text: "SESSION " + root.sessionName(root.sessionIndex).toUpperCase()
onClicked: root.nextSession() onClicked: root.nextSession()
} }
}
Row { Row {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 32 anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter anchors.margins: root.edgeMargin
spacing: 32 spacing: 28
FooterButton { Repeater {
visible: sddm.canReboot model: [
text: "REBOOT" { label: "Reboot", available: sddm.canReboot, action: "reboot" },
onClicked: sddm.reboot() { label: "Shut Down", available: sddm.canPowerOff, action: "powerOff" }
]
Text {
required property var modelData
visible: modelData.available
text: modelData.label
color: powerArea.containsMouse ? root.textColor : root.placeholderColor
font.family: root.fontFamily
font.pixelSize: root.smallFontSize
MouseArea {
id: powerArea
anchors.fill: parent
anchors.margins: -8
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: modelData.action === "reboot" ? sddm.reboot() : sddm.powerOff()
} }
FooterButton {
visible: sddm.canPowerOff
text: "SHUT DOWN"
onClicked: sddm.powerOff()
} }
} }
} }
Component.onCompleted: { Component.onCompleted: fieldInput.forceActiveFocus()
if (usernameField.text.length > 0) passwordField.input.forceActiveFocus()
else usernameField.input.forceActiveFocus()
}
} }