380 lines
15 KiB
QML
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|