// Config created by Keyitdev https://github.com/Keyitdev/sddm-astronaut-theme // Copyright (C) 2022-2026 Keyitdev // Based on https://github.com/MarianArlt/sddm-sugar-dark // Distributed under the GPLv3+ License https://www.gnu.org/licenses/gpl-3.0.html import QtQuick import QtQuick.Layouts import QtQuick.Controls Column { id: inputContainer Layout.fillWidth: true spacing: 0 property ComboBox exposeSession: sessionSelect.exposeSession property bool failed readonly property real passwordFieldTopMargin: (Number(config.PasswordFieldTopMargin) || 0) * rootHeightUnit readonly property real loginButtonTopMargin: (Number(config.LoginButtonTopMargin) || 0) * rootHeightUnit function findRegisteredUser(typedUser, matchRealName) { for (var i = 0; i < userRegistry.list.length; i++) { var candidate = userRegistry.list[i] if (typedUser === candidate.name || (matchRealName && typedUser === candidate.realName)) { return candidate } } return null } property url currentUserIcon: { var typedUser = username.text if (typedUser === "") { var highlighted = userRegistry.list[selectUser.currentIndex] return highlighted ? (highlighted.icon || "") : "" } var match = findRegisteredUser(typedUser, true) return match ? (match.icon || "") : "" } function triggerLogin() { var typedUser = username.text var match = findRegisteredUser(typedUser, config.UseRealName == "true") var targetUser = match ? match.name : typedUser sddm.login(targetUser, password.text, sessionSelect.selectedSession) } Item { id: errorMessageField height: rootHeightUnit * 1.5 width: parent.width / 2 anchors.horizontalCenter: parent.horizontalCenter Label { id: errorMessage width: parent.width horizontalAlignment: Text.AlignHCenter text: failed ? config.TranslateLoginFailedWarning || textConstants.loginFailed + "!" : keyboard.capsLock ? config.TranslateCapslockWarning || textConstants.capslockWarning : "" font.pixelSize: rootFontSize * 1.33 font.italic: true color: config.WarningColor opacity: 0 states: [ State { name: "fail" when: failed PropertyChanges { target: errorMessage opacity: 1 } }, State { name: "capslock" when: keyboard.capsLock PropertyChanges { target: errorMessage opacity: 1 } } ] transitions: [ Transition { PropertyAnimation { properties: "opacity" duration: 100 } } ] } } Item { id: usernameField height: username.height width: parent.width / 2 anchors.horizontalCenter: parent.horizontalCenter ComboBox { id: selectUser width: parent.height height: parent.height anchors.left: parent.left anchors.leftMargin: selectUser.height * 0.2 z: 2 model: userModel currentIndex: model.lastIndex textRole: config.UseRealName == "true" ? "realName" : "name" valueRole: "name" hoverEnabled: true displayText: config.UseRealName == "true" ? (currentText ? currentText : currentValue) : currentText onActivated: { username.text = displayText } property var popkey: config.RightToLeftLayout == "true" ? Qt.Key_Right : Qt.Key_Left Keys.onPressed: function(event) { if (event.key == Qt.Key_Down && !popup.opened) username.forceActiveFocus(); if ((event.key == Qt.Key_Up || event.key == popkey) && !popup.opened) popup.open(); } KeyNavigation.down: username KeyNavigation.right: username delegate: ItemDelegate { // minus padding width: popupHandler.width - (rootWidthUnit * 1.5) anchors.horizontalCenter: popupHandler.horizontalCenter topPadding: rootScaleUnit * 6 bottomPadding: rootScaleUnit * 6 leftPadding: rootScaleUnit * 12 rightPadding: rootScaleUnit * 12 contentItem: Text { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: config.UseRealName == "true" ? (model.realName || model.name) : model.name font.pixelSize: rootFontSize * 1.06 font.capitalization: Font.MixedCase font.family: root.font.family color: config.DropdownTextColor } background: Rectangle { color: selectUser.highlightedIndex === index ? config.DropdownSelectedBackgroundColor : "transparent" } } indicator: Button { id: usernameIcon width: selectUser.height * 1 height: parent.height anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter icon.height: parent.height * 0.4 icon.width: parent.height * 0.4 enabled: false icon.color: config.UserIconColor icon.source: Qt.resolvedUrl("../Assets/User.svg") background: Rectangle { color: "transparent" border.color: "transparent" } } background: Rectangle { color: "transparent" border.color: "transparent" } popup: Popup { id: popupHandler implicitHeight: contentItem.implicitHeight width: usernameField.width y: parent.height - username.height / 3 x: config.RightToLeftLayout == "true" ? (-loginButton.width + selectUser.width + selectUser.anchors.leftMargin) : 0 - selectUser.anchors.leftMargin rightMargin: config.RightToLeftLayout == "true" ? root.padding + usernameField.width / 2 : undefined padding: rootHeightUnit * 0.75 contentItem: ListView { implicitHeight: contentHeight + (rootHeightUnit * 1.5) clip: true model: selectUser.popup.visible ? selectUser.delegateModel : null currentIndex: selectUser.highlightedIndex ScrollIndicator.vertical: ScrollIndicator { } } background: Rectangle { radius: (rootScaleUnit * config.RoundCorners) / 2 color: config.DropdownBackgroundColor layer.enabled: true } enter: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1 } } } states: [ State { name: "pressed" when: selectUser.down PropertyChanges { target: usernameIcon icon.color: Qt.lighter(config.HoverUserIconColor, 1.1) } }, State { name: "hovered" when: selectUser.hovered PropertyChanges { target: usernameIcon icon.color: Qt.lighter(config.HoverUserIconColor, 1.2) } }, State { name: "focused" when: selectUser.activeFocus PropertyChanges { target: usernameIcon icon.color: config.HoverUserIconColor } } ] transitions: [ Transition { PropertyAnimation { properties: "color, border.color, icon.color" duration: 150 } } ] } TextField { id: username anchors.centerIn: parent height: rootHeightUnit * 3 width: parent.width font.pixelSize: rootFontSize * 1.33 horizontalAlignment: TextInput.AlignHCenter leftPadding: selectUser.width rightPadding: selectUser.width z: 1 text: config.ForceLastUser == "true" ? selectUser.displayText : null color: config.LoginFieldTextColor font.bold: true font.capitalization: Font.MixedCase placeholderText: config.TranslatePlaceholderUsername || textConstants.userName placeholderTextColor: config.PlaceholderTextColor selectByMouse: true renderType: Text.QtRendering onFocusChanged:{ if(focus) selectAll() } background: Rectangle { color: config.LoginFieldBackgroundColor opacity: 0.2 border.color: "transparent" border.width: parent.activeFocus ? (rootScaleUnit * 2) : (rootScaleUnit * 1) radius: rootScaleUnit * config.RoundCorners || 0 } onAccepted: triggerLogin() KeyNavigation.down: passwordIcon states: [ State { name: "focused" when: username.activeFocus PropertyChanges { target: username.background border.color: config.HighlightBorderColor } PropertyChanges { target: username color: Qt.lighter(config.LoginFieldTextColor, 1.15) } } ] } } Item { id: passwordFieldSpacer width: 1 height: passwordFieldTopMargin } Item { id: passwordField height: password.height width: parent.width / 2 anchors.horizontalCenter: parent.horizontalCenter Button { id: passwordIcon width: parent.height height: parent.height anchors.left: parent.left anchors.leftMargin: selectUser.height * 0.2 z: 2 icon.height: parent.height * 0.4 icon.width: parent.height * 0.4 icon.color: config.PasswordIconColor icon.source: Qt.resolvedUrl("../Assets/Password2.svg") background: Rectangle { color: "transparent" border.color: "transparent" } states: [ State { name: "visiblePasswordFocused" when: passwordIcon.checked && passwordIcon.activeFocus PropertyChanges { target: passwordIcon icon.source: Qt.resolvedUrl("../Assets/Password.svg") icon.color: config.HoverPasswordIconColor } }, State { name: "visiblePasswordHovered" when: passwordIcon.checked && passwordIcon.hovered PropertyChanges { target: passwordIcon icon.source: Qt.resolvedUrl("../Assets/Password.svg") icon.color: config.HoverPasswordIconColor } }, State { name: "visiblePassword" when: passwordIcon.checked PropertyChanges { target: passwordIcon icon.source: Qt.resolvedUrl("../Assets/Password.svg") } }, State { name: "hiddenPasswordFocused" when: passwordIcon.enabled && passwordIcon.activeFocus PropertyChanges { target: passwordIcon icon.source: Qt.resolvedUrl("../Assets/Password2.svg") icon.color: config.HoverPasswordIconColor } }, State { name: "hiddenPasswordHovered" when: passwordIcon.hovered PropertyChanges { target: passwordIcon icon.source: Qt.resolvedUrl("../Assets/Password2.svg") icon.color: config.HoverPasswordIconColor } } ] onClicked: toggle() Keys.onReturnPressed: toggle() Keys.onEnterPressed: toggle() KeyNavigation.down: password } TextField { id: password height: rootHeightUnit * 3 width: parent.width font.pixelSize: rootFontSize * 1.33 anchors.centerIn: parent horizontalAlignment: TextInput.AlignHCenter leftPadding: passwordIcon.width rightPadding: passwordIcon.width font.bold: true color: config.PasswordFieldTextColor focus: config.PasswordFocus == "true" echoMode: passwordIcon.checked ? TextInput.Normal : TextInput.Password placeholderText: config.TranslatePlaceholderPassword || textConstants.password placeholderTextColor: config.PlaceholderTextColor passwordCharacter: "•" passwordMaskDelay: config.ShowLastPasswordCharacter == "true" ? 1000 : undefined renderType: Text.QtRendering selectByMouse: true background: Rectangle { color: config.PasswordFieldBackgroundColor opacity: 0.2 border.color: "transparent" border.width: parent.activeFocus ? (rootScaleUnit * 2) : (rootScaleUnit * 1) radius: rootScaleUnit * config.RoundCorners || 0 } onAccepted: triggerLogin() KeyNavigation.down: loginButton } states: [ State { name: "focused" when: password.activeFocus PropertyChanges { target: password.background border.color: config.HighlightBorderColor } PropertyChanges { target: password color: Qt.lighter(config.LoginFieldTextColor, 1.15) } } ] transitions: [ Transition { PropertyAnimation { properties: "color, border.color" duration: 150 } } ] } Item { id: loginButtonSpacer width: 1 height: loginButtonTopMargin } Item { id: login height: loginButton.height width: parent.width / 2 anchors.horizontalCenter: parent.horizontalCenter visible: config.ShowLoginButton == "true" Button { id: loginButton height: rootHeightUnit * 3 implicitWidth: parent.width anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: config.TranslateLogin || textConstants.login enabled: config.AllowEmptyPassword == "true" || (username.text != "" && password.text != "") hoverEnabled: true contentItem: Text { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.bold: true font.pixelSize: rootFontSize * 1.33 font.family: root.font.family color: config.LoginButtonTextColor text: parent.text opacity: 0.5 } background: Rectangle { id: buttonBackground color: config.LoginButtonBackgroundColor opacity: 0.2 radius: rootScaleUnit * config.RoundCorners || 0 } states: [ State { name: "pressed" when: loginButton.down PropertyChanges { target: buttonBackground color: Qt.darker(config.LoginButtonBackgroundColor, 1.1) opacity: 1 } PropertyChanges { target: loginButton.contentItem } }, State { name: "hovered" when: loginButton.hovered PropertyChanges { target: buttonBackground color: Qt.lighter(config.LoginButtonBackgroundColor, 1.15) opacity: 1 } PropertyChanges { target: loginButton.contentItem opacity: 1 } }, State { name: "focused" when: loginButton.activeFocus PropertyChanges { target: buttonBackground color: Qt.lighter(config.LoginButtonBackgroundColor, 1.2) opacity: 1 } PropertyChanges { target: loginButton.contentItem opacity: 1 } }, State { name: "enabled" when: loginButton.enabled PropertyChanges { target: buttonBackground; color: config.LoginButtonBackgroundColor; opacity: 1 } PropertyChanges { target: loginButton.contentItem; opacity: 1 } } ] transitions: [ Transition { PropertyAnimation { properties: "opacity, color"; duration: 300 } } ] onClicked: triggerLogin() Keys.onReturnPressed: clicked() Keys.onEnterPressed: clicked() KeyNavigation.down: config.ShowSystemButtons == "true" ? systemButtons.children[0] : virtualKeyboard } } Item { id: userRegistry visible: false property var list: [] Repeater { model: userModel Item { Component.onCompleted: { var currentList = userRegistry.list currentList.push({ "name": model.name, "realName": model.realName || "", "icon": model.icon || "" }) userRegistry.list = currentList } } } } Connections { target: sddm function onLoginSucceeded() {} function onLoginFailed() { failed = true resetError.running ? resetError.stop() && resetError.start() : resetError.start() } } Timer { id: resetError interval: 2000 onTriggered: failed = false running: false } }