[plasma/plasma-login-manager] src/frontend/greeter/qml: frontend/greeter: remember last session separately for each user
Oliver Beard <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 79d5c144add8aa00a9cd1d93a2e793890d331d66 by Oliver Beard, on behalf of Jin Liu.
Committed on 22/07/2026 at 09:12.
Pushed by olib into branch 'master'.
frontend/greeter: remember last session separately for each user
The greeter now saves the last logged-in session separately for each user.
When a user icon is clicked, the session dropdown automatically selects
that user's last session (if available).
This improves the user experience when multiple users share the same device,
and using different desktop environments or window managers. Or in my
case, I created separate user accounts for Plasma release and dev builds.
The implementation is by replacing the single "lastLoggedInSession" string
in the greeter state config, with a JSON string that maps usernames to their
last sessions. Transistion from the old format is handled automatically.
M +65 -4 src/frontend/greeter/qml/GreeterState.qml
M +6 -0 src/frontend/greeter/qml/Login.qml
M +4 -31 src/frontend/greeter/qml/Main.qml
M +1 -3 src/frontend/greeter/qml/SessionButton.qml
https://invent.kde.org/plasma/plasma-login-manager/-/commit/79d5c144add8aa00a9cd1d93a2e793890d331d66
diff --git a/src/frontend/greeter/qml/GreeterState.qml b/src/frontend/greeter/qml/GreeterState.qml
index 68395e94..941d5000 100644
--- a/src/frontend/greeter/qml/GreeterState.qml
+++ b/src/frontend/greeter/qml/GreeterState.qml
@@ -37,12 +37,20 @@ Item {
readonly property int beyondUserLimit: PlasmaLogin.UserModel.rowCount() === 0 || PlasmaLogin.UserModel.rowCount() > 7
property int loginState: GreeterState.LoginState.UserList
- onLoginStateChanged: clearPasswords();
+
+ onLoginStateChanged: {
+ clearPasswords();
+
+ if (greeterState.loginState == GreeterState.LoginState.UserList) {
+ let user = PlasmaLogin.UserModel.data(PlasmaLogin.UserModel.index(greeterState.userListIndex, 0), PlasmaLogin.UserModel.NameRole);
+ updateSessionForUser(user);
+ }
+ }
property int sessionIndex: {
// indexOfData will return -1 if passed an empty string, which these are by default
let preselectedSessionIndex = PlasmaLogin.SessionModel.indexOfData(PlasmaLogin.Settings.preselectedSession, PlasmaLogin.SessionModel.FileNameRole);
- let lastLoggedInSessionIndex = PlasmaLogin.SessionModel.indexOfData(PlasmaLogin.StateConfig.lastLoggedInSession, PlasmaLogin.SessionModel.FileNameRole);
+ let lastLoggedInSessionIndex = PlasmaLogin.SessionModel.indexOfData(getLastLoggedInSessionForUser(PlasmaLogin.StateConfig.lastLoggedInUser), PlasmaLogin.SessionModel.FileNameRole);
if (preselectedSessionIndex != -1) {
return preselectedSessionIndex;
@@ -66,11 +74,21 @@ Item {
return 0;
}
}
+
+ onUserListIndexChanged: {
+ let user = PlasmaLogin.UserModel.data(PlasmaLogin.UserModel.index(greeterState.userListIndex, 0), PlasmaLogin.UserModel.NameRole);
+ updateSessionForUser(user);
+ }
+
property string userListPassword: ""
property string userPromptUsername: ""
property string userPromptPassword: ""
+ onUserPromptUsernameChanged: {
+ updateSessionForUser(greeterState.userPromptUsername);
+ }
+
property bool showPassword: false
// Shared functionality
@@ -140,19 +158,62 @@ Item {
property string lastLoggedInUser
property string lastLoggedInSession
- function handleLoginRequest(username, password, sessionType, sessionFileName) {
+ function handleLoginRequest(username, password) {
greeterState.lastLoggedInUser = username;
+
+ let sessionType = PlasmaLogin.SessionModel.data(PlasmaLogin.SessionModel.index(greeterState.sessionIndex, 0), PlasmaLogin.SessionModel.TypeRole)
+ let sessionFileName = PlasmaLogin.SessionModel.data(PlasmaLogin.SessionModel.index(greeterState.sessionIndex, 0), PlasmaLogin.SessionModel.FileNameRole)
greeterState.lastLoggedInSession = sessionFileName;
PlasmaLogin.Authenticator.login(username, password, sessionType, sessionFileName);
}
+ property var lastLoggedInSessions: {
+ let lastLoggedInUser = PlasmaLogin.StateConfig.lastLoggedInUser;
+ let sessionsJson = PlasmaLogin.StateConfig.lastLoggedInSession;
+ let result = {};
+ if (sessionsJson !== "") {
+ try {
+ result = JSON.parse(sessionsJson);
+ } catch (e) {
+ if (lastLoggedInUser !== "") {
+ result[lastLoggedInUser] = sessionsJson;
+ }
+ }
+ }
+
+ for (let user in result) {
+ if (PlasmaLogin.UserModel.indexOfData(user, PlasmaLogin.UserModel.NameRole) === -1) {
+ delete result[user];
+ }
+ }
+
+ return result;
+ }
+
+ function getLastLoggedInSessionForUser(username) {
+ return greeterState.lastLoggedInSessions[username] ?? -1;
+ }
+
+ function setLastLoggedInSessionForUser(username, sessionPath) {
+ greeterState.lastLoggedInSessions[username] = sessionPath;
+ PlasmaLogin.StateConfig.lastLoggedInSession = JSON.stringify(greeterState.lastLoggedInSessions);
+ }
+
+ function updateSessionForUser(username) {
+ let session = getLastLoggedInSessionForUser(username);
+ let lastLoggedInSessionIndex = PlasmaLogin.SessionModel.indexOfData(session, PlasmaLogin.SessionModel.FileNameRole);
+ if (lastLoggedInSessionIndex != -1) {
+ greeterState.sessionIndex = lastLoggedInSessionIndex;
+ }
+ }
+
Connections {
target: PlasmaLogin.Authenticator
function onLoginSucceeded() {
PlasmaLogin.StateConfig.lastLoggedInUser = greeterState.lastLoggedInUser;
- PlasmaLogin.StateConfig.lastLoggedInSession = greeterState.lastLoggedInSession;
+ setLastLoggedInSessionForUser(greeterState.lastLoggedInUser, greeterState.lastLoggedInSession);
PlasmaLogin.StateConfig.save();
}
}
diff --git a/src/frontend/greeter/qml/Login.qml b/src/frontend/greeter/qml/Login.qml
index 5b69e34f..a6499e5f 100644
--- a/src/frontend/greeter/qml/Login.qml
+++ b/src/frontend/greeter/qml/Login.qml
@@ -95,6 +95,7 @@ SessionManagementScreen {
}
RowLayout {
+ id: passwordRow
Layout.fillWidth: true
PlasmaExtras.PasswordField {
@@ -155,6 +156,11 @@ SessionManagementScreen {
}
}
+ SessionButton {
+ id: sessionButton
+ Layout.maximumWidth: passwordRow.width
+ }
+
// Synchronise state
Item {
id: sync
diff --git a/src/frontend/greeter/qml/Main.qml b/src/frontend/greeter/qml/Main.qml
index 773ae4e8..c06626e6 100644
--- a/src/frontend/greeter/qml/Main.qml
+++ b/src/frontend/greeter/qml/Main.qml
@@ -211,7 +211,7 @@ Item {
visible: !userListComponent.showUsernamePrompt
}]
- onLoginRequest: (username, password) => root.handleLoginRequest(username, password, sessionButton.currentSessionType, sessionButton.currentSessionFileName)
+ onLoginRequest: (username, password) => root.handleLoginRequest(username, password)
}
readonly property real zoomFactor: 1.5
@@ -309,7 +309,7 @@ Item {
}
}
- onLoginRequest: (username, password) => root.handleLoginRequest(username, password, sessionButton.currentSessionType, sessionButton.currentSessionFileName)
+ onLoginRequest: (username, password) => root.handleLoginRequest(username, password)
//actionItemsVisible: !inputPanel.keyboardActive
actionItems: [
@@ -373,33 +373,6 @@ Item {
id: keyboardButton
}
- SessionButton {
- id: sessionButton
-
- onSessionChanged: {
- // NOTE: This won't work for userPromptComponent: we might
- // want a function to focus the correct password box, but
- // userPromptComponent has both that and the user box
- // Perhaps we need to store which one last had focus
-
- // Otherwise the password field loses focus and virtual keyboard
- // keystrokes get eaten
- userListComponent.mainPasswordBox.forceActiveFocus();
- }
-
- Layout.fillHeight: true
- containmentMask: Item {
- parent: sessionButton
- anchors.fill: parent
- /*
- anchors.leftMargin: virtualKeyboardButton.visible || keyboardButton.visible
- ? 0 : -footer.anchors.margins
- */
- anchors.leftMargin: 0
- anchors.bottomMargin: -footer.anchors.margins
- }
- }
-
Item {
Layout.fillWidth: true
}
@@ -408,10 +381,10 @@ Item {
}
}
- function handleLoginRequest(username, password, sessionType, sessionFileName) {
+ function handleLoginRequest(username, password) {
root.notificationMessage = "";
// GreeterState handles updating stateconfig with user/session of successful login
- PlasmaLogin.GreeterState.handleLoginRequest(username, password, sessionType, sessionFileName);
+ PlasmaLogin.GreeterState.handleLoginRequest(username, password);
}
Connections {
diff --git a/src/frontend/greeter/qml/SessionButton.qml b/src/frontend/greeter/qml/SessionButton.qml
index cba47f69..b99c4ea9 100644
--- a/src/frontend/greeter/qml/SessionButton.qml
+++ b/src/frontend/greeter/qml/SessionButton.qml
@@ -17,9 +17,6 @@ PlasmaComponents.ToolButton {
property int currentIndex: PlasmaLogin.GreeterState.sessionIndex
- readonly property int currentSessionType: instantiator.model.data(instantiator.model.index(currentIndex, 0), PlasmaLogin.SessionModel.TypeRole)
- readonly property string currentSessionFileName: instantiator.model.data(instantiator.model.index(currentIndex, 0), PlasmaLogin.SessionModel.FileNameRole)
-
// Count is used as instantiator may not have made items yet
text: i18nd("plasma_login", "Desktop Session: %1", instantiator.count > currentIndex ? instantiator.objectAt(currentIndex).text : "")
visible: menu.count > 1
@@ -66,6 +63,7 @@ PlasmaComponents.ToolButton {
function onSessionIndexChanged() {
if (root.currentIndex != PlasmaLogin.GreeterState.sessionIndex) {
root.currentIndex = PlasmaLogin.GreeterState.sessionIndex;
+ menu.currentIndex = root.currentIndex;
}
}
}