Files
sddm-theme/themes/sm1tee/components/LoginScreen.qml
Ваше Имя ed9ba99412 update
2025-06-30 23:19:29 +03:00

380 lines
15 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SddmComponents
Item {
id: loginScreen
signal close
signal toggleLayoutPopup
state: "normal"
property bool stateChanging: false
function safeStateChange(newState) { // This is probably overkill, but whatever
if (!stateChanging) {
stateChanging = true;
state = newState;
stateChanging = false;
}
}
onStateChanged: {
if (state === "normal") {
resetFocus();
}
}
readonly property alias password: password
readonly property alias loginButton: loginButton
readonly property alias loginContainer: loginContainer
property bool showKeyboard: !Config.virtualKeyboardStartHidden
// Login info
property int sessionIndex: 0
property int userIndex: 0
property string userName: ""
property string userRealName: ""
property string userIcon: ""
property bool userNeedsPassword: false
function login() {
if (password.text.length > 0 || !userNeedsPassword) {
safeStateChange("authenticating");
sddm.login(userName, password.text, sessionIndex);
}
}
Connections {
function onLoginSucceeded() {
loginContainer.scale = 0.0;
}
function onLoginFailed() {
safeStateChange("normal");
loginMessage.warn(textConstants.loginFailed || "Login failed", "error");
password.text = "";
}
function onInformationMessage(message) {
loginMessage.warn(message, "error");
}
target: sddm
}
// FIX: Critical connections memory leak prevention?
Component.onDestruction: {
if (typeof connections !== 'undefined') {
connections.target = null;
}
}
function updateCapsLock() {
if (root.capsLockOn && loginScreen.state !== "authenticating") {
loginMessage.warn(textConstants.capslockWarning || "Caps Lock is on", "warning");
} else {
loginMessage.clear();
}
}
function resetFocus() {
if (loginScreen.userNeedsPassword) {
password.input.forceActiveFocus();
} else {
loginButton.forceActiveFocus();
}
}
Item {
id: loginContainer
width: Config.loginAreaPosition === "left" || Config.loginAreaPosition === "right" ? (Config.avatarActiveSize + Config.usernameMargin + loginArea.width) : userSelector.width
height: childrenRect.height
scale: 0.5 // Initial animation
Behavior on scale {
enabled: Config.enableAnimations
NumberAnimation {
duration: 200
}
}
// LoginArea position
Component.onCompleted: {
if (Config.loginAreaPosition === "left") {
anchors.verticalCenter = parent.verticalCenter;
if (Config.loginAreaMargin === -1) {
anchors.horizontalCenter = parent.horizontalCenter;
} else {
anchors.left = parent.left;
anchors.leftMargin = Config.loginAreaMargin;
}
} else if (Config.loginAreaPosition === "right") {
anchors.verticalCenter = parent.verticalCenter;
if (Config.loginAreaMargin === -1) {
anchors.horizontalCenter = parent.horizontalCenter;
} else {
anchors.right = parent.right;
anchors.rightMargin = Config.loginAreaMargin;
}
} else {
anchors.horizontalCenter = parent.horizontalCenter;
if (Config.loginAreaMargin === -1) {
anchors.verticalCenter = parent.verticalCenter;
} else {
anchors.top = parent.top;
anchors.topMargin = Config.loginAreaMargin;
}
}
}
UserSelector {
id: userSelector
listUsers: loginScreen.state === "selectingUser"
enabled: loginScreen.state !== "authenticating"
activeFocusOnTab: true
orientation: Config.loginAreaPosition === "left" || Config.loginAreaPosition === "right" ? "vertical" : "horizontal"
width: orientation === "horizontal" ? loginScreen.width - Config.loginAreaMargin * 2 : Config.avatarActiveSize
height: orientation === "horizontal" ? Config.avatarActiveSize : loginScreen.height - Config.loginAreaMargin * 2
onOpenUserList: {
safeStateChange("selectingUser");
}
onCloseUserList: {
safeStateChange("normal");
loginScreen.resetFocus(); // resetFocus with escape even if the selector is not open
}
onUserChanged: (index, name, realName, icon, needsPassword) => {
loginScreen.userIndex = index;
loginScreen.userName = name;
loginScreen.userRealName = realName;
loginScreen.userIcon = icon;
loginScreen.userNeedsPassword = needsPassword;
}
Component.onCompleted: {
anchors.top = parent.top;
if (Config.loginAreaPosition === "left") {
anchors.left = parent.left;
} else if (Config.loginAreaPosition === "right") {
anchors.right = parent.right;
}
}
}
Item {
id: loginLayout
height: activeUserName.height + Config.passwordInputMarginTop + loginArea.height
width: loginArea.width > activeUserName.width ? loginArea.width : activeUserName.width
// LoginArea alignment
Component.onCompleted: {
if (Config.loginAreaPosition === "left") {
anchors.verticalCenter = parent.verticalCenter;
anchors.left = userSelector.right;
anchors.leftMargin = Config.usernameMargin;
} else if (Config.loginAreaPosition === "right") {
anchors.verticalCenter = parent.verticalCenter;
anchors.right = userSelector.left;
anchors.rightMargin = Config.usernameMargin;
} else {
anchors.top = userSelector.bottom;
anchors.topMargin = Config.usernameMargin;
anchors.horizontalCenter = parent.horizontalCenter;
}
}
Text {
id: activeUserName
font.family: Config.usernameFontFamily
font.weight: Config.usernameFontWeight
font.pixelSize: Config.usernameFontSize
color: Config.usernameColor
text: loginScreen.userRealName || loginScreen.userName || ""
Component.onCompleted: {
anchors.top = parent.top;
if (Config.loginAreaPosition === "left") {
anchors.left = parent.left;
} else if (Config.loginAreaPosition === "right") {
anchors.right = parent.right;
} else {
anchors.horizontalCenter = parent.horizontalCenter;
}
}
}
RowLayout {
id: loginArea
height: Config.passwordInputHeight
spacing: Config.loginButtonMarginLeft
visible: loginScreen.state !== "authenticating"
Component.onCompleted: {
anchors.top = activeUserName.bottom;
anchors.topMargin = Config.passwordInputMarginTop;
if (Config.loginAreaPosition === "left") {
anchors.left = parent.left;
} else if (Config.loginAreaPosition === "right") {
anchors.right = parent.right;
} else {
anchors.horizontalCenter = parent.horizontalCenter;
}
}
PasswordInput {
id: password
Layout.alignment: Qt.AlignHCenter
enabled: loginScreen.state !== "selectingUser" && loginScreen.state !== "authenticating" && loginScreen.state === "normal"
visible: loginScreen.userNeedsPassword
onAccepted: {
loginScreen.login();
}
}
IconButton {
id: loginButton
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: width // Fix button not resizing when label updates
height: password.height
visible: !Config.loginButtonHideIfNotNeeded || !loginScreen.userNeedsPassword
enabled: loginScreen.state !== "selectingUser" && loginScreen.state !== "authenticating"
activeFocusOnTab: true
icon: Config.getIcon(Config.loginButtonIcon)
label: textConstants.login ? textConstants.login.toUpperCase() : "LOGIN"
showLabel: Config.loginButtonShowTextIfNoPassword && !loginScreen.userNeedsPassword
tooltipText: !Config.tooltipsDisableLoginButton && (!Config.loginButtonShowTextIfNoPassword || loginScreen.userNeedsPassword) ? (textConstants.login || "Login") : ""
iconSize: Config.loginButtonIconSize
fontFamily: Config.loginButtonFontFamily
fontSize: Config.loginButtonFontSize
fontWeight: Config.loginButtonFontWeight
contentColor: Config.loginButtonContentColor
activeContentColor: Config.loginButtonActiveContentColor
backgroundColor: Config.loginButtonBackgroundColor
backgroundOpacity: Config.loginButtonBackgroundOpacity
activeBackgroundColor: Config.loginButtonActiveBackgroundColor
activeBackgroundOpacity: Config.loginButtonActiveBackgroundOpacity
borderSize: Config.loginButtonBorderSize
borderColor: Config.loginButtonBorderColor
borderRadiusLeft: password.visible ? Config.loginButtonBorderRadiusLeft : Config.loginButtonBorderRadiusRight
borderRadiusRight: Config.loginButtonBorderRadiusRight
onClicked: {
loginScreen.login();
}
Behavior on x {
enabled: Config.enableAnimations
NumberAnimation {
duration: 150
}
}
}
}
Spinner {
id: spinner
visible: loginScreen.state === "authenticating"
opacity: visible ? 1.0 : 0.0
Component.onCompleted: {
anchors.top = activeUserName.bottom;
anchors.topMargin = Config.passwordInputMarginTop;
if (Config.loginAreaPosition === "left") {
anchors.left = parent.left;
} else if (Config.loginAreaPosition === "right") {
anchors.right = parent.right;
} else {
anchors.horizontalCenter = parent.horizontalCenter;
}
}
}
Text {
id: loginMessage
property bool capslockWarning: false
font.pixelSize: Config.warningMessageFontSize
font.family: Config.warningMessageFontFamily
font.weight: Config.warningMessageFontWeight
color: Config.warningMessageNormalColor
visible: text !== "" && loginScreen.state !== "authenticating" && (capslockWarning ? loginScreen.userNeedsPassword : true)
opacity: visible ? 1.0 : 0.0
anchors.top: loginArea.bottom
anchors.topMargin: visible ? Config.warningMessageMarginTop : 0
Component.onCompleted: {
if (root.capsLockOn)
loginMessage.warn(textConstants.capslockWarning || "Caps Lock is on", "warning");
if (Config.loginAreaPosition === "left") {
anchors.left = parent.left;
} else if (Config.loginAreaPosition === "right") {
anchors.right = parent.right;
} else {
anchors.horizontalCenter = parent.horizontalCenter;
}
}
Behavior on anchors.topMargin {
enabled: Config.enableAnimations
NumberAnimation {
duration: 150
}
}
Behavior on opacity {
enabled: Config.enableAnimations
NumberAnimation {
duration: 150
}
}
function warn(message, type) {
clear();
text = message;
color = type === "error" ? Config.warningMessageErrorColor : (type === "warning" ? Config.warningMessageWarningColor : Config.warningMessageNormalColor);
if (message === (textConstants.capslockWarning || "Caps Lock is on"))
capslockWarning = true;
}
function clear() {
text = "";
capslockWarning = false;
}
}
}
}
MenuArea {}
VirtualKeyboard {}
Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) {
if (loginScreen.state === "authenticating") {
event.accepted = false;
return;
}
if (Config.lockScreenDisplay) {
loginScreen.close();
}
password.text = "";
} else if (event.key === Qt.Key_CapsLock) {
root.capsLockOn = !root.capsLockOn;
}
event.accepted = true;
}
MouseArea {
id: closeUserSelectorMouseArea
z: -1
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (loginScreen.state === "selectingUser") {
safeStateChange("normal");
}
}
onWheel: event => {
if (loginScreen.state === "selectingUser") {
if (event.angleDelta.y < 0) {
userSelector.nextUser();
} else {
userSelector.prevUser();
}
}
}
}
}